diff options
author | Casey Marshall <csm@gnu.org> | 2006-01-26 02:25:07 +0000 |
---|---|---|
committer | Casey Marshall <csm@gnu.org> | 2006-01-26 02:25:07 +0000 |
commit | bd5a522ce73b442dd07099c264bf251e6d8d6a63 (patch) | |
tree | b1cdc8f04689d9ea082589d3a7e61fef847ac1fa /gnu/javax/crypto/sasl | |
parent | bf20436907439f2c47dc22eea41b3d3d5808aa74 (diff) | |
download | classpath-bd5a522ce73b442dd07099c264bf251e6d8d6a63.tar.gz |
2006-01-25 Casey Marshall <csm@gnu.org>
Merging GNU Crypto and Jessie.
* NEWS: mention the merge in the 0.21 notes.
* gnu/classpath/debug/Component.java (SSL_APPLICATION): removed.
(SSL_RECORD_LAYER): new constants.
* gnu/java/security/provider/Gnu.java (<init>): add new algorithms
to provider.
* resource/java/security/classpath.security: add new providers.
* gnu/javax/crypto/assembly/Assembly.java,
gnu/javax/crypto/assembly/Cascade.java,
gnu/javax/crypto/assembly/CascadeStage.java,
gnu/javax/crypto/assembly/CascadeTransformer.java,
gnu/javax/crypto/assembly/DeflateTransformer.java,
gnu/javax/crypto/assembly/Direction.java,
gnu/javax/crypto/assembly/LoopbackTransformer.java,
gnu/javax/crypto/assembly/ModeStage.java,
gnu/javax/crypto/assembly/Operation.java,
gnu/javax/crypto/assembly/PaddingTransformer.java,
gnu/javax/crypto/assembly/Stage.java,
gnu/javax/crypto/assembly/Transformer.java,
gnu/javax/crypto/assembly/TransformerException.java,
gnu/javax/crypto/cipher/Anubis.java,
gnu/javax/crypto/cipher/BaseCipher.java,
gnu/javax/crypto/cipher/Blowfish.java,
gnu/javax/crypto/cipher/Cast5.java,
gnu/javax/crypto/cipher/CipherFactory.java,
gnu/javax/crypto/cipher/DES.java,
gnu/javax/crypto/cipher/IBlockCipher.java,
gnu/javax/crypto/cipher/IBlockCipherSpi.java,
gnu/javax/crypto/cipher/Khazad.java,
gnu/javax/crypto/cipher/NullCipher.java,
gnu/javax/crypto/cipher/Rijndael.java,
gnu/javax/crypto/cipher/Serpent.java,
gnu/javax/crypto/cipher/Square.java,
gnu/javax/crypto/cipher/TripleDES.java,
gnu/javax/crypto/cipher/Twofish.java,
gnu/javax/crypto/cipher/WeakKeyException.java,
gnu/javax/crypto/jce/GnuCrypto.java,
gnu/javax/crypto/jce/GnuSasl.java,
gnu/javax/crypto/jce/PBKDF2SecretKeyFactory.java,
gnu/javax/crypto/jce/cipher/AESSpi.java,
gnu/javax/crypto/jce/cipher/ARCFourSpi.java,
gnu/javax/crypto/jce/cipher/AnubisSpi.java,
gnu/javax/crypto/jce/cipher/BlowfishSpi.java,
gnu/javax/crypto/jce/cipher/Cast5Spi.java,
gnu/javax/crypto/jce/cipher/CipherAdapter.java,
gnu/javax/crypto/jce/cipher/DESSpi.java,
gnu/javax/crypto/jce/cipher/KhazadSpi.java,
gnu/javax/crypto/jce/cipher/NullCipherSpi.java,
gnu/javax/crypto/jce/cipher/PBES2.java,
gnu/javax/crypto/jce/cipher/RijndaelSpi.java,
gnu/javax/crypto/jce/cipher/SerpentSpi.java,
gnu/javax/crypto/jce/cipher/SquareSpi.java,
gnu/javax/crypto/jce/cipher/TripleDESSpi.java,
gnu/javax/crypto/jce/cipher/TwofishSpi.java,
gnu/javax/crypto/jce/key/AnubisKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/AnubisSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/BlowfishKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/BlowfishSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/Cast5KeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/Cast5SecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/DESKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/DESSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/DESedeSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/KhazadKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/KhazadSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/RijndaelKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/RijndaelSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/SecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/SecretKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/SerpentKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/SerpentSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/SquareKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/SquareSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/key/TripleDESKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/TwofishKeyGeneratorImpl.java,
gnu/javax/crypto/jce/key/TwofishSecretKeyFactoryImpl.java,
gnu/javax/crypto/jce/keyring/GnuKeyring.java,
gnu/javax/crypto/jce/mac/HMacHavalSpi.java,
gnu/javax/crypto/jce/mac/HMacMD2Spi.java,
gnu/javax/crypto/jce/mac/HMacMD4Spi.java,
gnu/javax/crypto/jce/mac/HMacMD5Spi.java,
gnu/javax/crypto/jce/mac/HMacRipeMD128Spi.java,
gnu/javax/crypto/jce/mac/HMacRipeMD160Spi.java,
gnu/javax/crypto/jce/mac/HMacSHA160Spi.java,
gnu/javax/crypto/jce/mac/HMacSHA256Spi.java,
gnu/javax/crypto/jce/mac/HMacSHA384Spi.java,
gnu/javax/crypto/jce/mac/HMacSHA512Spi.java,
gnu/javax/crypto/jce/mac/HMacTigerSpi.java,
gnu/javax/crypto/jce/mac/HMacWhirlpoolSpi.java,
gnu/javax/crypto/jce/mac/MacAdapter.java,
gnu/javax/crypto/jce/mac/OMacAnubisImpl.java,
gnu/javax/crypto/jce/mac/OMacBlowfishImpl.java,
gnu/javax/crypto/jce/mac/OMacCast5Impl.java,
gnu/javax/crypto/jce/mac/OMacDESImpl.java,
gnu/javax/crypto/jce/mac/OMacImpl.java,
gnu/javax/crypto/jce/mac/OMacKhazadImpl.java,
gnu/javax/crypto/jce/mac/OMacRijndaelImpl.java,
gnu/javax/crypto/jce/mac/OMacSerpentImpl.java,
gnu/javax/crypto/jce/mac/OMacSquareImpl.java,
gnu/javax/crypto/jce/mac/OMacTripleDESImpl.java,
gnu/javax/crypto/jce/mac/OMacTwofishImpl.java,
gnu/javax/crypto/jce/mac/TMMH16Spi.java,
gnu/javax/crypto/jce/mac/UHash32Spi.java,
gnu/javax/crypto/jce/mac/UMac32Spi.java,
gnu/javax/crypto/jce/params/BlockCipherParameters.java,
gnu/javax/crypto/jce/params/DEREncodingException.java,
gnu/javax/crypto/jce/params/DERReader.java,
gnu/javax/crypto/jce/params/DERWriter.java,
gnu/javax/crypto/jce/prng/ARCFourRandomSpi.java,
gnu/javax/crypto/jce/prng/CSPRNGSpi.java,
gnu/javax/crypto/jce/prng/FortunaImpl.java,
gnu/javax/crypto/jce/prng/ICMRandomSpi.java,
gnu/javax/crypto/jce/prng/UMacRandomSpi.java,
gnu/javax/crypto/jce/spec/BlockCipherParameterSpec.java,
gnu/javax/crypto/jce/spec/TMMHParameterSpec.java,
gnu/javax/crypto/jce/spec/UMac32ParameterSpec.java,
gnu/javax/crypto/key/BaseKeyAgreementParty.java,
gnu/javax/crypto/key/GnuSecretKey.java,
gnu/javax/crypto/key/IKeyAgreementParty.java,
gnu/javax/crypto/key/IncomingMessage.java,
gnu/javax/crypto/key/KeyAgreementException.java,
gnu/javax/crypto/key/KeyAgreementFactory.java,
gnu/javax/crypto/key/OutgoingMessage.java,
gnu/javax/crypto/key/dh/DHKeyPairRawCodec.java,
gnu/javax/crypto/key/dh/DiffieHellmanKeyAgreement.java,
gnu/javax/crypto/key/dh/DiffieHellmanReceiver.java,
gnu/javax/crypto/key/dh/DiffieHellmanSender.java,
gnu/javax/crypto/key/dh/ElGamalKeyAgreement.java,
gnu/javax/crypto/key/dh/ElGamalReceiver.java,
gnu/javax/crypto/key/dh/ElGamalSender.java,
gnu/javax/crypto/key/dh/GnuDHKey.java,
gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java,
gnu/javax/crypto/key/dh/GnuDHPrivateKey.java,
gnu/javax/crypto/key/dh/GnuDHPublicKey.java,
gnu/javax/crypto/key/dh/RFC2631.java,
gnu/javax/crypto/key/srp6/SRP6Host.java,
gnu/javax/crypto/key/srp6/SRP6KeyAgreement.java,
gnu/javax/crypto/key/srp6/SRP6SaslClient.java,
gnu/javax/crypto/key/srp6/SRP6SaslServer.java,
gnu/javax/crypto/key/srp6/SRP6TLSClient.java,
gnu/javax/crypto/key/srp6/SRP6TLSServer.java,
gnu/javax/crypto/key/srp6/SRP6User.java,
gnu/javax/crypto/key/srp6/SRPAlgorithm.java,
gnu/javax/crypto/key/srp6/SRPKey.java,
gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java,
gnu/javax/crypto/key/srp6/SRPKeyPairRawCodec.java,
gnu/javax/crypto/key/srp6/SRPPrivateKey.java,
gnu/javax/crypto/key/srp6/SRPPublicKey.java,
gnu/javax/crypto/keyring/AuthenticatedEntry.java,
gnu/javax/crypto/keyring/BaseKeyring.java,
gnu/javax/crypto/keyring/BinaryDataEntry.java,
gnu/javax/crypto/keyring/CertPathEntry.java,
gnu/javax/crypto/keyring/CertificateEntry.java,
gnu/javax/crypto/keyring/CompressedEntry.java,
gnu/javax/crypto/keyring/EncryptedEntry.java,
gnu/javax/crypto/keyring/Entry.java,
gnu/javax/crypto/keyring/EnvelopeEntry.java,
gnu/javax/crypto/keyring/GnuPrivateKeyring.java,
gnu/javax/crypto/keyring/GnuPublicKeyring.java,
gnu/javax/crypto/keyring/IKeyring.java,
gnu/javax/crypto/keyring/IPrivateKeyring.java,
gnu/javax/crypto/keyring/IPublicKeyring.java,
gnu/javax/crypto/keyring/MalformedKeyringException.java,
gnu/javax/crypto/keyring/MaskableEnvelopeEntry.java,
gnu/javax/crypto/keyring/MeteredInputStream.java,
gnu/javax/crypto/keyring/PasswordAuthenticatedEntry.java,
gnu/javax/crypto/keyring/PasswordEncryptedEntry.java,
gnu/javax/crypto/keyring/PasswordProtectedEntry.java,
gnu/javax/crypto/keyring/PrimitiveEntry.java,
gnu/javax/crypto/keyring/PrivateKeyEntry.java,
gnu/javax/crypto/keyring/Properties.java,
gnu/javax/crypto/keyring/PublicKeyEntry.java,
gnu/javax/crypto/mac/BaseMac.java,
gnu/javax/crypto/mac/HMac.java,
gnu/javax/crypto/mac/HMacFactory.java,
gnu/javax/crypto/mac/IMac.java,
gnu/javax/crypto/mac/MacFactory.java,
gnu/javax/crypto/mac/MacInputStream.java,
gnu/javax/crypto/mac/MacOutputStream.java,
gnu/javax/crypto/mac/OMAC.java,
gnu/javax/crypto/mac/TMMH16.java,
gnu/javax/crypto/mac/UHash32.java,
gnu/javax/crypto/mac/UMac32.java,
gnu/javax/crypto/mode/BaseMode.java,
gnu/javax/crypto/mode/CBC.java,
gnu/javax/crypto/mode/CFB.java,
gnu/javax/crypto/mode/CTR.java,
gnu/javax/crypto/mode/EAX.java,
gnu/javax/crypto/mode/ECB.java,
gnu/javax/crypto/mode/IAuthenticatedMode.java,
gnu/javax/crypto/mode/ICM.java,
gnu/javax/crypto/mode/IMode.java,
gnu/javax/crypto/mode/ModeFactory.java,
gnu/javax/crypto/mode/OFB.java,
gnu/javax/crypto/pad/BasePad.java,
gnu/javax/crypto/pad/IPad.java,
gnu/javax/crypto/pad/PKCS1_V1_5.java,
gnu/javax/crypto/pad/PKCS7.java,
gnu/javax/crypto/pad/PadFactory.java,
gnu/javax/crypto/pad/SSL3.java,
gnu/javax/crypto/pad/TBC.java,
gnu/javax/crypto/pad/TLS1.java,
gnu/javax/crypto/pad/WrongPaddingException.java,
gnu/javax/crypto/prng/ARCFour.java,
gnu/javax/crypto/prng/CSPRNG.java,
gnu/javax/crypto/prng/Fortuna.java,
gnu/javax/crypto/prng/ICMGenerator.java,
gnu/javax/crypto/prng/IPBE.java,
gnu/javax/crypto/prng/PBKDF2.java,
gnu/javax/crypto/prng/PRNGFactory.java,
gnu/javax/crypto/prng/UMacGenerator.java,
gnu/javax/crypto/sasl/AuthInfo.java,
gnu/javax/crypto/sasl/AuthInfoProviderFactory.java,
gnu/javax/crypto/sasl/ClientFactory.java,
gnu/javax/crypto/sasl/ClientMechanism.java,
gnu/javax/crypto/sasl/ConfidentialityException.java,
gnu/javax/crypto/sasl/IAuthInfoProvider.java,
gnu/javax/crypto/sasl/IAuthInfoProviderFactory.java,
gnu/javax/crypto/sasl/IllegalMechanismStateException.java,
gnu/javax/crypto/sasl/InputBuffer.java,
gnu/javax/crypto/sasl/IntegrityException.java,
gnu/javax/crypto/sasl/NoSuchMechanismException.java,
gnu/javax/crypto/sasl/NoSuchUserException.java,
gnu/javax/crypto/sasl/OutputBuffer.java,
gnu/javax/crypto/sasl/SaslEncodingException.java,
gnu/javax/crypto/sasl/SaslInputStream.java,
gnu/javax/crypto/sasl/SaslOutputStream.java,
gnu/javax/crypto/sasl/SaslUtil.java,
gnu/javax/crypto/sasl/ServerFactory.java,
gnu/javax/crypto/sasl/ServerMechanism.java,
gnu/javax/crypto/sasl/UserAlreadyExistsException.java,
gnu/javax/crypto/sasl/anonymous/AnonymousClient.java,
gnu/javax/crypto/sasl/anonymous/AnonymousServer.java,
gnu/javax/crypto/sasl/anonymous/AnonymousUtil.java,
gnu/javax/crypto/sasl/crammd5/CramMD5AuthInfoProvider.java,
gnu/javax/crypto/sasl/crammd5/CramMD5Client.java,
gnu/javax/crypto/sasl/crammd5/CramMD5Registry.java,
gnu/javax/crypto/sasl/crammd5/CramMD5Server.java,
gnu/javax/crypto/sasl/crammd5/CramMD5Util.java,
gnu/javax/crypto/sasl/crammd5/PasswordFile.java,
gnu/javax/crypto/sasl/plain/PasswordFile.java,
gnu/javax/crypto/sasl/plain/PlainAuthInfoProvider.java,
gnu/javax/crypto/sasl/plain/PlainClient.java,
gnu/javax/crypto/sasl/plain/PlainRegistry.java,
gnu/javax/crypto/sasl/plain/PlainServer.java,
gnu/javax/crypto/sasl/srp/CALG.java,
gnu/javax/crypto/sasl/srp/ClientStore.java,
gnu/javax/crypto/sasl/srp/IALG.java,
gnu/javax/crypto/sasl/srp/KDF.java,
gnu/javax/crypto/sasl/srp/PasswordFile.java,
gnu/javax/crypto/sasl/srp/SRP.java,
gnu/javax/crypto/sasl/srp/SRPAuthInfoProvider.java,
gnu/javax/crypto/sasl/srp/SRPClient.java,
gnu/javax/crypto/sasl/srp/SRPRegistry.java,
gnu/javax/crypto/sasl/srp/SRPServer.java,
gnu/javax/crypto/sasl/srp/SecurityContext.java,
gnu/javax/crypto/sasl/srp/ServerStore.java,
gnu/javax/crypto/sasl/srp/StoreEntry.java,
gnu/javax/net/ssl/Base64.java,
gnu/javax/net/ssl/EntropySource.java,
gnu/javax/net/ssl/NullManagerParameters.java,
gnu/javax/net/ssl/PrivateCredentials.java,
gnu/javax/net/ssl/SRPManagerParameters.java,
gnu/javax/net/ssl/SRPTrustManager.java,
gnu/javax/net/ssl/StaticTrustAnchors.java,
gnu/javax/net/ssl/provider/Alert.java,
gnu/javax/net/ssl/provider/AlertException.java,
gnu/javax/net/ssl/provider/Certificate.java,
gnu/javax/net/ssl/provider/CertificateRequest.java,
gnu/javax/net/ssl/provider/CertificateType.java,
gnu/javax/net/ssl/provider/CertificateVerify.java,
gnu/javax/net/ssl/provider/CipherSuite.java,
gnu/javax/net/ssl/provider/ClientHello.java,
gnu/javax/net/ssl/provider/ClientKeyExchange.java,
gnu/javax/net/ssl/provider/CompressionMethod.java,
gnu/javax/net/ssl/provider/Constructed.java,
gnu/javax/net/ssl/provider/ContentType.java,
gnu/javax/net/ssl/provider/Context.java,
gnu/javax/net/ssl/provider/DiffieHellman.java,
gnu/javax/net/ssl/provider/DigestInputStream.java,
gnu/javax/net/ssl/provider/DigestOutputStream.java,
gnu/javax/net/ssl/provider/Enumerated.java,
gnu/javax/net/ssl/provider/Extension.java,
gnu/javax/net/ssl/provider/Extensions.java,
gnu/javax/net/ssl/provider/Finished.java,
gnu/javax/net/ssl/provider/GNUSecurityParameters.java,
gnu/javax/net/ssl/provider/Handshake.java,
gnu/javax/net/ssl/provider/JCESecurityParameters.java,
gnu/javax/net/ssl/provider/JDBCSessionContext.java,
gnu/javax/net/ssl/provider/Jessie.java,
gnu/javax/net/ssl/provider/JessieDHPrivateKey.java,
gnu/javax/net/ssl/provider/JessieDHPublicKey.java,
gnu/javax/net/ssl/provider/JessieRSAPrivateKey.java,
gnu/javax/net/ssl/provider/JessieRSAPublicKey.java,
gnu/javax/net/ssl/provider/KeyPool.java,
gnu/javax/net/ssl/provider/MacException.java,
gnu/javax/net/ssl/provider/OverflowException.java,
gnu/javax/net/ssl/provider/PRNG.java,
gnu/javax/net/ssl/provider/ProtocolVersion.java,
gnu/javax/net/ssl/provider/Random.java,
gnu/javax/net/ssl/provider/RecordInput.java,
gnu/javax/net/ssl/provider/RecordInputStream.java,
gnu/javax/net/ssl/provider/RecordOutputStream.java,
gnu/javax/net/ssl/provider/RecordingInputStream.java,
gnu/javax/net/ssl/provider/SRPTrustManagerFactory.java,
gnu/javax/net/ssl/provider/SSLHMac.java,
gnu/javax/net/ssl/provider/SSLRSASignature.java,
gnu/javax/net/ssl/provider/SSLRandom.java,
gnu/javax/net/ssl/provider/SSLServerSocket.java,
gnu/javax/net/ssl/provider/SSLServerSocketFactory.java,
gnu/javax/net/ssl/provider/SSLSocket.java,
gnu/javax/net/ssl/provider/SSLSocketFactory.java,
gnu/javax/net/ssl/provider/SSLSocketInputStream.java,
gnu/javax/net/ssl/provider/SSLSocketOutputStream.java,
gnu/javax/net/ssl/provider/SecurityParameters.java,
gnu/javax/net/ssl/provider/ServerHello.java,
gnu/javax/net/ssl/provider/ServerKeyExchange.java,
gnu/javax/net/ssl/provider/Session.java,
gnu/javax/net/ssl/provider/SessionContext.java,
gnu/javax/net/ssl/provider/Signature.java,
gnu/javax/net/ssl/provider/SynchronizedRandom.java,
gnu/javax/net/ssl/provider/TLSHMac.java,
gnu/javax/net/ssl/provider/TLSRandom.java,
gnu/javax/net/ssl/provider/Util.java,
gnu/javax/net/ssl/provider/X509KeyManagerFactory.java,
gnu/javax/net/ssl/provider/X509TrustManagerFactory.java,
gnu/javax/net/ssl/provider/XMLSessionContext.java,
gnu/javax/security/auth/Password.java,
gnu/javax/security/auth/callback/AWTCallbackHandler.java,
gnu/javax/security/auth/callback/AbstractCallbackHandler.java,
gnu/javax/security/auth/callback/ConsoleCallbackHandler.java,
gnu/javax/security/auth/callback/DefaultCallbackHandler.java,
gnu/javax/security/auth/callback/GnuCallbacks.java,
gnu/javax/security/auth/callback/SwingCallbackHandler.java,
gnu/java/security/Registry.java,
gnu/java/security/Properties.java,
gnu/java/security/hash/BaseHash.java,
gnu/java/security/hash/HashFactory.java,
gnu/java/security/hash/Haval.java,
gnu/java/security/hash/IMessageDigest.java,
gnu/java/security/hash/MD2.java,
gnu/java/security/hash/MD4.java,
gnu/java/security/hash/MD5.java,
gnu/java/security/hash/RipeMD128.java,
gnu/java/security/hash/RipeMD160.java,
gnu/java/security/hash/Sha160.java,
gnu/java/security/hash/Sha256.java,
gnu/java/security/hash/Sha384.java,
gnu/java/security/hash/Sha512.java,
gnu/java/security/hash/Tiger.java,
gnu/java/security/hash/Whirlpool.java,
gnu/java/security/jce/hash/HavalSpi.java,
gnu/java/security/jce/hash/MD2Spi.java,
gnu/java/security/jce/hash/MD4Spi.java,
gnu/java/security/jce/hash/MD5Spi.java,
gnu/java/security/jce/hash/MessageDigestAdapter.java,
gnu/java/security/jce/hash/RipeMD128Spi.java,
gnu/java/security/jce/hash/RipeMD160Spi.java,
gnu/java/security/jce/hash/Sha160Spi.java,
gnu/java/security/jce/hash/Sha256Spi.java,
gnu/java/security/jce/hash/Sha384Spi.java,
gnu/java/security/jce/hash/Sha512Spi.java,
gnu/java/security/jce/hash/TigerSpi.java,
gnu/java/security/jce/hash/WhirlpoolSpi.java,
gnu/java/security/jce/prng/HavalRandomSpi.java,
gnu/java/security/jce/prng/MD2RandomSpi.java,
gnu/java/security/jce/prng/MD4RandomSpi.java,
gnu/java/security/jce/prng/MD5RandomSpi.java,
gnu/java/security/jce/prng/RipeMD128RandomSpi.java,
gnu/java/security/jce/prng/RipeMD160RandomSpi.java,
gnu/java/security/jce/prng/SecureRandomAdapter.java,
gnu/java/security/jce/prng/Sha160RandomSpi.java,
gnu/java/security/jce/prng/Sha256RandomSpi.java,
gnu/java/security/jce/prng/Sha384RandomSpi.java,
gnu/java/security/jce/prng/Sha512RandomSpi.java,
gnu/java/security/jce/prng/TigerRandomSpi.java,
gnu/java/security/jce/prng/WhirlpoolRandomSpi.java,
gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java,
gnu/java/security/jce/sig/DSSRawSignatureSpi.java,
gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java,
gnu/java/security/jce/sig/RSAKeyPairGeneratorSpi.java,
gnu/java/security/jce/sig/RSAPSSRawSignatureSpi.java,
gnu/java/security/jce/sig/SignatureAdapter.java,
gnu/java/security/key/IKeyPairCodec.java,
gnu/java/security/key/IKeyPairGenerator.java,
gnu/java/security/key/KeyPairCodecFactory.java,
gnu/java/security/key/KeyPairGeneratorFactory.java,
gnu/java/security/key/dss/DSSKey.java,
gnu/java/security/key/dss/DSSKeyPairGenerator.java,
gnu/java/security/key/dss/DSSKeyPairRawCodec.java,
gnu/java/security/key/dss/DSSPrivateKey.java,
gnu/java/security/key/dss/DSSPublicKey.java,
gnu/java/security/key/dss/FIPS186.java,
gnu/java/security/key/rsa/GnuRSAKey.java,
gnu/java/security/key/rsa/GnuRSAPrivateKey.java,
gnu/java/security/key/rsa/GnuRSAPublicKey.java,
gnu/java/security/key/rsa/RSAKeyPairGenerator.java,
gnu/java/security/key/rsa/RSAKeyPairRawCodec.java,
gnu/java/security/prng/BasePRNG.java,
gnu/java/security/prng/EntropySource.java,
gnu/java/security/prng/IRandom.java,
gnu/java/security/prng/LimitReachedException.java,
gnu/java/security/prng/MDGenerator.java,
gnu/java/security/prng/PRNGFactory.java,
gnu/java/security/prng/RandomEvent.java,
gnu/java/security/prng/RandomEventListener.java,
gnu/java/security/sig/BaseSignature.java,
gnu/java/security/sig/ISignature.java,
gnu/java/security/sig/ISignatureCodec.java,
gnu/java/security/sig/SignatureFactory.java,
gnu/java/security/sig/dss/DSSSignature.java,
gnu/java/security/sig/dss/DSSSignatureRawCodec.java,
gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java,
gnu/java/security/sig/rsa/EMSA_PKCS1_V1_5.java,
gnu/java/security/sig/rsa/EMSA_PSS.java,
gnu/java/security/sig/rsa/RSA.java,
gnu/java/security/sig/rsa/RSAPKCS1V1_5Signature.java,
gnu/java/security/sig/rsa/RSAPSSSignature.java,
gnu/java/security/sig/rsa/RSAPSSSignatureRawCodec.java,
gnu/java/security/util/Base64.java,
gnu/java/security/util/ExpirableObject.java,
gnu/java/security/util/Prime2.java,
gnu/java/security/util/Sequence.java,
gnu/java/security/util/SimpleList.java,
gnu/java/security/util/Util.java,
resource/gnu/javax/security/auth/callback/MessagesBundle.properties:
new files imported from GNU Crypto and Jessie.
Diffstat (limited to 'gnu/javax/crypto/sasl')
47 files changed, 10793 insertions, 0 deletions
diff --git a/gnu/javax/crypto/sasl/AuthInfo.java b/gnu/javax/crypto/sasl/AuthInfo.java new file mode 100644 index 000000000..1e942559d --- /dev/null +++ b/gnu/javax/crypto/sasl/AuthInfo.java @@ -0,0 +1,143 @@ +/* AuthInfo.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.Registry; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.StringTokenizer; + +/** + * A static class for creating {@link IAuthInfoProvider} providers. It + * transparently locates and uses any provider instances, based on the value + * assigned to the System property with the key + * <code>gnu.crypto.sasl.auth.info.provider.pkgs</code>. If more than one is + * specified they SHOULD be separated with a vertical bar character. Please note + * that the GNU provider is always added last to the list, disregarding whether + * it was mentioned or not in the value of that property, or if it that property + * was not defined. + */ +public class AuthInfo +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private static final ArrayList factories = new ArrayList(); + static + { + IAuthInfoProviderFactory ours = new AuthInfoProviderFactory(); + // if SASL_AUTH_INFO_PROVIDER_PKGS is defined then parse it + String clazz; + String pkgs = System.getProperty(Registry.SASL_AUTH_INFO_PROVIDER_PKGS, + null); + if (pkgs != null) + { + for (StringTokenizer st = new StringTokenizer(pkgs, "|"); st.hasMoreTokens();) + { + clazz = st.nextToken(); + if (!"gnu.crypto.sasl".equals(clazz)) + { + clazz += ".AuthInfoProviderFactory"; + try + { + IAuthInfoProviderFactory factory = (IAuthInfoProviderFactory) Class.forName( + clazz).newInstance(); + factories.add(factory); + } + catch (ClassCastException ignored) + { + } + catch (ClassNotFoundException ignored) + { + } + catch (InstantiationException ignored) + { + } + catch (IllegalAccessException ignored) + { + } + } + } + } + // always add ours last; unless it's already there + if (!factories.contains(ours)) + { + factories.add(ours); + } + } + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** Trivial constructor to enforce Singleton pattern. */ + private AuthInfo() + { + super(); + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * A convenience method to return the authentication information provider + * for a designated SASL mechnanism. It goes through all the installed + * provider factories, one at a time, and attempts to return a new instance + * of the provider for the designated mechanism. It stops at the first + * factory returning a non-null provider. + * + * @param mechanism the name of a SASL mechanism. + * @return an implementation that provides {@link IAuthInfoProvider} for that + * mechanism; or <code>null</code> if none found. + */ + public static IAuthInfoProvider getProvider(String mechanism) + { + for (Iterator it = factories.iterator(); it.hasNext();) + { + IAuthInfoProviderFactory factory = (IAuthInfoProviderFactory) it.next(); + IAuthInfoProvider result = factory.getInstance(mechanism); + if (result != null) + { + return result; + } + } + return null; + } +} diff --git a/gnu/javax/crypto/sasl/AuthInfoProviderFactory.java b/gnu/javax/crypto/sasl/AuthInfoProviderFactory.java new file mode 100644 index 000000000..6ba5fc562 --- /dev/null +++ b/gnu/javax/crypto/sasl/AuthInfoProviderFactory.java @@ -0,0 +1,89 @@ +/* AuthInfoProviderFactory.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.crammd5.CramMD5AuthInfoProvider; +import gnu.javax.crypto.sasl.plain.PlainAuthInfoProvider; +import gnu.javax.crypto.sasl.srp.SRPAuthInfoProvider; + +/** + * The concrete SASL authentication information provider factory. + */ +public class AuthInfoProviderFactory implements IAuthInfoProviderFactory +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + // implicit 0-args constructor + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // IAuthInfoProviderFactory interface implementation ----------------------- + + public IAuthInfoProvider getInstance(String mechanism) + { + if (mechanism == null) + { + return null; + } + mechanism = mechanism.trim().toUpperCase(); + if (mechanism.startsWith(Registry.SASL_SRP_MECHANISM)) + { + return new SRPAuthInfoProvider(); + } + if (mechanism.equals(Registry.SASL_CRAM_MD5_MECHANISM)) + { + return new CramMD5AuthInfoProvider(); + } + if (mechanism.equals(Registry.SASL_PLAIN_MECHANISM)) + { + return new PlainAuthInfoProvider(); + } + return null; + } +} diff --git a/gnu/javax/crypto/sasl/ClientFactory.java b/gnu/javax/crypto/sasl/ClientFactory.java new file mode 100644 index 000000000..ef184632c --- /dev/null +++ b/gnu/javax/crypto/sasl/ClientFactory.java @@ -0,0 +1,210 @@ +/* ClientFactory.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.anonymous.AnonymousClient; +import gnu.javax.crypto.sasl.crammd5.CramMD5Client; +import gnu.javax.crypto.sasl.plain.PlainClient; +import gnu.javax.crypto.sasl.srp.SRPClient; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.HashMap; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslException; + +/** + * The implementation of {@link SaslClientFactory}. + */ +public class ClientFactory implements SaslClientFactory +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + // implicit 0-arguments constructor + + // Class methods + // ------------------------------------------------------------------------- + + public static final Set getNames() + { + return Collections.unmodifiableSet(new HashSet( + Arrays.asList(getNamesInternal(null)))); + } + + private static final String[] getNamesInternal(Map props) + { + String[] all = new String[] { Registry.SASL_SRP_MECHANISM, + Registry.SASL_CRAM_MD5_MECHANISM, + Registry.SASL_PLAIN_MECHANISM, + Registry.SASL_ANONYMOUS_MECHANISM }; + + if (props == null) + { + return all; + } + if (hasPolicy(Sasl.POLICY_PASS_CREDENTIALS, props)) + { + return new String[0]; + } + + List result = new ArrayList(all.length); + ; + for (int i = 0; i < all.length;) + { + result.add(all[i++]); + } + + if (hasPolicy(Sasl.POLICY_NOPLAINTEXT, props)) + { + result.remove(Registry.SASL_PLAIN_MECHANISM); + } + if (hasPolicy(Sasl.POLICY_NOACTIVE, props)) + { + result.remove(Registry.SASL_CRAM_MD5_MECHANISM); + result.remove(Registry.SASL_PLAIN_MECHANISM); + } + if (hasPolicy(Sasl.POLICY_NODICTIONARY, props)) + { + result.remove(Registry.SASL_CRAM_MD5_MECHANISM); + result.remove(Registry.SASL_PLAIN_MECHANISM); + } + if (hasPolicy(Sasl.POLICY_NOANONYMOUS, props)) + { + result.remove(Registry.SASL_ANONYMOUS_MECHANISM); + } + if (hasPolicy(Sasl.POLICY_FORWARD_SECRECY, props)) + { + result.remove(Registry.SASL_CRAM_MD5_MECHANISM); + result.remove(Registry.SASL_ANONYMOUS_MECHANISM); + result.remove(Registry.SASL_PLAIN_MECHANISM); + } + return (String[]) result.toArray(new String[0]); + } + + public static final ClientMechanism getInstance(String mechanism) + { + if (mechanism == null) + { + return null; + } + mechanism = mechanism.trim().toUpperCase(); + if (mechanism.equals(Registry.SASL_SRP_MECHANISM)) + { + return new SRPClient(); + } + if (mechanism.equals(Registry.SASL_CRAM_MD5_MECHANISM)) + { + return new CramMD5Client(); + } + if (mechanism.equals(Registry.SASL_PLAIN_MECHANISM)) + { + return new PlainClient(); + } + if (mechanism.equals(Registry.SASL_ANONYMOUS_MECHANISM)) + { + return new AnonymousClient(); + } + return null; + } + + // Instance methods + // ------------------------------------------------------------------------- + + public SaslClient createSaslClient(String[] mechanisms, + String authorisationID, String protocol, + String serverName, Map props, + CallbackHandler cbh) throws SaslException + { + ClientMechanism result = null; + String mechanism; + for (int i = 0; i < mechanisms.length; i++) + { + mechanism = mechanisms[i]; + result = getInstance(mechanism); + if (result != null) + { + break; + } + } + + if (result != null) + { + HashMap attributes = new HashMap(); + if (props != null) + { + attributes.putAll(props); + } + attributes.put(Registry.SASL_AUTHORISATION_ID, authorisationID); + attributes.put(Registry.SASL_PROTOCOL, protocol); + attributes.put(Registry.SASL_SERVER_NAME, serverName); + attributes.put(Registry.SASL_CALLBACK_HANDLER, cbh); + + result.init(attributes); + return result; + } + + throw new SaslException( + "No supported mechanism found in given mechanism list"); + } + + public String[] getMechanismNames(Map props) + { + return getNamesInternal(props); + } + + private static boolean hasPolicy(String propertyName, Map props) + { + return "true".equalsIgnoreCase(String.valueOf(props.get(propertyName))); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/ClientMechanism.java b/gnu/javax/crypto/sasl/ClientMechanism.java new file mode 100644 index 000000000..45873ae6b --- /dev/null +++ b/gnu/javax/crypto/sasl/ClientMechanism.java @@ -0,0 +1,365 @@ +/* ClientMechanism.java -- + Copyright (C) 2003, 2005, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.Registry; + +import java.util.HashMap; +import java.util.Map; +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * <p>A base class to facilitate implementing SASL client-side mechanisms.</p> + */ +public abstract class ClientMechanism implements SaslClient +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + /** Name of this mechanism. */ + protected String mechanism; + + /** The authorisation identity. */ + protected String authorizationID; + + /** Name of protocol using this mechanism. */ + protected String protocol; + + /** Name of server to authenticate to. */ + protected String serverName; + + /** Properties of qualities desired for this mechanism. */ + protected Map properties; + + /** Callback handler to use with this mechanism instance. */ + protected CallbackHandler handler; + + /** Channel binding data to use with this mechanism instance. */ + protected byte[] channelBinding; + + /** Whether authentication phase is completed (true) or not (false). */ + protected boolean complete = false; + + /** The state of the authentication automaton. */ + protected int state = -1; + + // Constructor(s) + // ------------------------------------------------------------------------- + + protected ClientMechanism(final String mechanism) + { + super(); + + this.mechanism = mechanism; + this.state = -1; + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods to be implemented by concrete subclasses --------------- + + protected abstract void initMechanism() throws SaslException; + + protected abstract void resetMechanism() throws SaslException; + + // javax.security.sasl.SaslClient interface implementation ----------------- + + public abstract byte[] evaluateChallenge(byte[] challenge) + throws SaslException; + + public abstract boolean hasInitialResponse(); + + public boolean isComplete() + { + return complete; + } + + public byte[] unwrap(final byte[] incoming, final int offset, final int len) + throws SaslException + { + if (!isComplete()) + { + throw new IllegalMechanismStateException(); + } + return this.engineUnwrap(incoming, offset, len); + } + + public byte[] wrap(final byte[] outgoing, final int offset, final int len) + throws SaslException + { + if (!isComplete()) + { + throw new IllegalMechanismStateException(); + } + return this.engineWrap(outgoing, offset, len); + } + + public String getMechanismName() + { + return mechanism; + } + + public Object getNegotiatedProperty(final String propName) + { + if (!isComplete()) + { + throw new IllegalStateException(); + } + if (Sasl.QOP.equals(propName)) + { + return getNegotiatedQOP(); + } + if (Sasl.STRENGTH.equals(propName)) + { + return getNegotiatedStrength(); + } + if (Sasl.SERVER_AUTH.equals(propName)) + { + return getNegotiatedServerAuth(); + } + if (Sasl.MAX_BUFFER.equals(propName)) + { + return getNegotiatedMaxBuffer(); + } + if (Sasl.RAW_SEND_SIZE.equals(propName)) + { + return getNegotiatedRawSendSize(); + } + if (Sasl.POLICY_NOPLAINTEXT.equals(propName)) + { + return getNegotiatedPolicyNoPlainText(); + } + if (Sasl.POLICY_NOACTIVE.equals(propName)) + { + return getNegotiatedPolicyNoActive(); + } + if (Sasl.POLICY_NODICTIONARY.equals(propName)) + { + return getNegotiatedPolicyNoDictionary(); + } + if (Sasl.POLICY_NOANONYMOUS.equals(propName)) + { + return getNegotiatedPolicyNoAnonymous(); + } + if (Sasl.POLICY_FORWARD_SECRECY.equals(propName)) + { + return getNegotiatedPolicyForwardSecrecy(); + } + if (Sasl.POLICY_PASS_CREDENTIALS.equals(propName)) + { + return getNegotiatedPolicyPassCredentials(); + } + if (Sasl.REUSE.equals(propName)) + { + return getReuse(); + } + return null; + } + + public void dispose() throws SaslException + { + } + + // other Instance methods -------------------------------------------------- + + public String getAuthorizationID() + { + return authorizationID; + } + + protected String getNegotiatedQOP() + { + return Registry.QOP_AUTH; + } + + protected String getNegotiatedStrength() + { + return Registry.STRENGTH_LOW; + } + + protected String getNegotiatedServerAuth() + { + return Registry.SERVER_AUTH_FALSE; + } + + protected String getNegotiatedMaxBuffer() + { + return null; + } + + protected String getNegotiatedRawSendSize() + { + return String.valueOf(Registry.SASL_BUFFER_MAX_LIMIT); + } + + protected String getNegotiatedPolicyNoPlainText() + { + return null; + } + + protected String getNegotiatedPolicyNoActive() + { + return null; + } + + protected String getNegotiatedPolicyNoDictionary() + { + return null; + } + + protected String getNegotiatedPolicyNoAnonymous() + { + return null; + } + + protected String getNegotiatedPolicyForwardSecrecy() + { + return null; + } + + protected String getNegotiatedPolicyPassCredentials() + { + return null; + } + + protected String getReuse() + { + return Registry.REUSE_FALSE; + } + + protected byte[] engineUnwrap(final byte[] incoming, final int offset, + final int len) throws SaslException + { + final byte[] result = new byte[len]; + System.arraycopy(incoming, offset, result, 0, len); + return result; + } + + protected byte[] engineWrap(final byte[] outgoing, final int offset, + final int len) throws SaslException + { + final byte[] result = new byte[len]; + System.arraycopy(outgoing, offset, result, 0, len); + return result; + } + + /** + * <p>Initialises the mechanism with designated attributes. Permissible names + * and values are mechanism specific.</p> + * + * @param attributes a set of name-value pairs that describes the desired + * future behaviour of this instance. + * @throws IllegalMechanismStateException if the instance is already + * initialised. + * @throws SaslException if an exception occurs during the process. + */ + public void init(final Map attributes) throws SaslException + { + if (state != -1) + { + throw new IllegalMechanismStateException("init()"); + } + + if (properties == null) + { + properties = new HashMap(); + } + else + { + properties.clear(); + } + if (attributes != null) + { + authorizationID = (String) attributes.get(Registry.SASL_AUTHORISATION_ID); + protocol = (String) attributes.get(Registry.SASL_PROTOCOL); + serverName = (String) attributes.get(Registry.SASL_SERVER_NAME); + handler = (CallbackHandler) attributes.get(Registry.SASL_CALLBACK_HANDLER); + channelBinding = (byte[]) attributes.get(Registry.SASL_CHANNEL_BINDING); + properties.putAll(attributes); + } + else + { + handler = null; + } + + if (authorizationID == null) + { + authorizationID = ""; + } + if (protocol == null) + { + protocol = ""; + } + if (serverName == null) + { + serverName = ""; + } + if (channelBinding == null) + { + channelBinding = new byte[0]; + } + initMechanism(); + complete = false; + state = 0; + } + + /** + * <p>Resets the mechanism instance for re-initialisation and use with other + * characteristics.</p> + * + * @throws SaslException if an exception occurs during the process. + */ + public void reset() throws SaslException + { + resetMechanism(); + properties.clear(); + authorizationID = protocol = serverName = null; + channelBinding = null; + complete = false; + state = -1; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/ConfidentialityException.java b/gnu/javax/crypto/sasl/ConfidentialityException.java new file mode 100644 index 000000000..561827d8d --- /dev/null +++ b/gnu/javax/crypto/sasl/ConfidentialityException.java @@ -0,0 +1,84 @@ +/* ConfidentialityException.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import javax.security.sasl.SaslException; + +/** + * Used by mechanisms that offer a security services layer, this checked + * exception is thrown to indicate that a violation has occured during the + * processing of a <i>confidentiality</i> protection filter. + * + * @version $Revision: 1.1 $ + */ +public class ConfidentialityException extends SaslException +{ + + /** + * Constructs a new instance of <code>ConfidentialityException</code> with no + * detail message. + */ + public ConfidentialityException() + { + super(); + } + + /** + * Constructs a new instance of <code>ConfidentialityException</code> with + * the specified detail message. + * + * @param s the detail message. + */ + public ConfidentialityException(String s) + { + super(s); + } + + /** + * Constructs a new instance of <code>ConfidentialityException</code> with a + * detailed message and a root exception. + * + * @param s possibly null additional detail about the exception. + * @param x a possibly null root exception that caused this one. + */ + public ConfidentialityException(String s, Throwable x) + { + super(s, x); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/IAuthInfoProvider.java b/gnu/javax/crypto/sasl/IAuthInfoProvider.java new file mode 100644 index 000000000..2b913a137 --- /dev/null +++ b/gnu/javax/crypto/sasl/IAuthInfoProvider.java @@ -0,0 +1,117 @@ +/* IAuthInfoProvider.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import java.util.Map; + +import javax.security.sasl.AuthenticationException; + +/** + * The visible methods of any authentication information provider. + */ +public interface IAuthInfoProvider +{ + + // Constants + // ------------------------------------------------------------------------- + + // Methods + // ------------------------------------------------------------------------- + + /** + * Activates (initialises) this provider instance. SHOULD be the first method + * invoked on the provider. + * + * @param context a collection of name-value bindings describing the + * activation context. + * @throws AuthenticationException if an exception occurs during the operation. + */ + void activate(Map context) throws AuthenticationException; + + /** + * Passivates (releases) this provider instance. SHOULD be the last method + * invoked on the provider. Once it is done, no other method may be invoked + * on the same instance before it is <i>activated</i> agains. + * + * @throws AuthenticationException if an exception occurs during the operation. + */ + void passivate() throws AuthenticationException; + + /** + * Checks if a user with a designated name is known to this provider. + * + * @param userName the name of a user to check. + * @return <code>true</code> if the user with the designated name is known to + * this provider; <code>false</code> otherwise. + * @throws AuthenticationException if an exception occurs during the operation. + */ + boolean contains(String userName) throws AuthenticationException; + + /** + * Returns a collection of information about a designated user. The contents + * of the returned map is provider-specific of name-to-value mappings. + * + * @param userID a map of name-to-value bindings that fully describe a user. + * @return a collection of information about the designated user. + * @throws AuthenticationException if an exception occurs during the operation. + */ + Map lookup(Map userID) throws AuthenticationException; + + /** + * Updates the credentials of a designated user. + * + * @param userCredentials a map of name-to-value bindings that fully describe + * a user, including per new credentials. + * @throws AuthenticationException if an exception occurs during the operation. + */ + void update(Map userCredentials) throws AuthenticationException; + + /** + * A provider may operate in more than mode; e.g. SRP-II caters for user + * credentials computed in more than one message digest algorithm. This + * method returns the set of name-to-value bindings describing the mode of + * the provider. + * + * @param mode a unique identifier describing the operational mode. + * @return a collection of name-to-value bindings describing the designated + * mode. + * @throws AuthenticationException if an exception occurs during the operation. + */ + Map getConfiguration(String mode) throws AuthenticationException; +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/IAuthInfoProviderFactory.java b/gnu/javax/crypto/sasl/IAuthInfoProviderFactory.java new file mode 100644 index 000000000..e630b8da1 --- /dev/null +++ b/gnu/javax/crypto/sasl/IAuthInfoProviderFactory.java @@ -0,0 +1,62 @@ +/* IAuthInfoProviderFactory.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +/** + * The visible method of every authentication information provider factory. + */ +public interface IAuthInfoProviderFactory +{ + + // Constants + // ------------------------------------------------------------------------- + + // Methods + // ------------------------------------------------------------------------- + + /** + * Returns an implementation of a provider for a designated mechanism + * capable of honouring {@link IAuthInfoProvider} requests. + * + * @param mechanism the unique name of a mechanism. + * @return an implementation of {@link IAuthInfoProvider} for that mechanism + * or <code>null</code> if none found. + */ + IAuthInfoProvider getInstance(String mechanism); +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/IllegalMechanismStateException.java b/gnu/javax/crypto/sasl/IllegalMechanismStateException.java new file mode 100644 index 000000000..94d9269a1 --- /dev/null +++ b/gnu/javax/crypto/sasl/IllegalMechanismStateException.java @@ -0,0 +1,86 @@ +/* IllegalMechanismStateException.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import javax.security.sasl.AuthenticationException; + +/** + * A checked exception thrown to indicate that an operation that should be + * invoked on a completed mechanism was invoked but the authentication phase of + * that mechanism was not completed yet, or that an operation that should be + * invoked on incomplete mechanisms was invoked but the authentication phase of + * that mechanism was already completed. + * + * @version $Revision: 1.1 $ + */ +public class IllegalMechanismStateException extends AuthenticationException +{ + + /** + * Constructs a new instance of <code>IllegalMechanismStateException</code> + * with no detail message. + */ + public IllegalMechanismStateException() + { + super(); + } + + /** + * Constructs a new instance of <code>IllegalMechanismStateException</code> + * with the specified detail message. + * + * @param detail the detail message. + */ + public IllegalMechanismStateException(String detail) + { + super(detail); + } + + /** + * Constructs a new instance of <code>IllegalMechanismStateException</code> + * with the specified detail message, and cause. + * + * @param detail the detail message. + * @param ex the original cause. + */ + public IllegalMechanismStateException(String detail, Throwable ex) + { + super(detail, ex); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/InputBuffer.java b/gnu/javax/crypto/sasl/InputBuffer.java new file mode 100644 index 000000000..a64ea3e0e --- /dev/null +++ b/gnu/javax/crypto/sasl/InputBuffer.java @@ -0,0 +1,339 @@ +/* InputBuffer.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.Registry; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; + +/** + * <p>The implementation of an incoming SASL buffer.</p> + * + * <p>The data elements this class caters for are described in [1].</p> + * + * <p>References:</p> + * <ol> + * <li><a href="http://www.ietf.org/internet-drafts/draft-burdis-cat-srp-sasl-09.txt"> + * Secure Remote Password Authentication Mechanism</a>;<br/> + * draft-burdis-cat-srp-sasl-09,<br/> + * <a href="mailto:keith@rucus.ru.ac.za">Keith Burdis</a> and + * <a href="mailto:raif@forge.com.au">Raïf S. Naffah</a>.</li> + * </ol> + */ +public class InputBuffer +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + /** The internal buffer stream containing the buffer's contents. */ + protected ByteArrayInputStream in; + + /** The length of the buffer, according to its header. */ + protected int length; + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** + * <p>Constructs a SASL buffer given the buffer's encoded form, including its + * header bytes.</p> + * + * @param frame the encoded form, including the header bytes, of a SASL buffer. + * @throws SaslEncodingException if the buffer is malformed. + */ + public InputBuffer(byte[] frame) throws SaslEncodingException + { + this(); + + if (frame.length < 4) + { + throw new SaslEncodingException("SASL buffer header too short"); + } + + length = (frame[0] & 0xFF) << 24 | (frame[1] & 0xFF) << 16 + | (frame[2] & 0xFF) << 8 | (frame[3] & 0xFF); + if (length > Registry.SASL_BUFFER_MAX_LIMIT || length < 0) + { + throw new SaslEncodingException("SASL buffer size limit exceeded"); + } + + in = new ByteArrayInputStream(frame, 4, length); + } + + /** Trivial private constructor for use by the class method. */ + private InputBuffer() + { + super(); + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns an instance of a SASL buffer given the buffer's encoded contents, + * excluding the buffer's header bytes.</p> + * + * <p>Calls the method with the same name and three arguments as: + * <code>getInstance(raw, 0, raw.length)</code>. + * + * @param raw the encoded form, excluding the header bytes, of a SASL buffer. + * @return a new instance of {@link InputBuffer}. + */ + public static InputBuffer getInstance(byte[] raw) + { + return getInstance(raw, 0, raw.length); + } + + /** + * <p>Returns an instance of a SASL buffer given the buffer's encoded + * contents, excluding the buffer's header bytes.</p> + * + * @param raw the encoded form, excluding the header bytes, of a SASL buffer. + * @param offset offset where to start using raw bytes from. + * @param len number of bytes to use. + * @return a new instance of {@link InputBuffer}. + */ + public static InputBuffer getInstance(byte[] raw, int offset, int len) + { + InputBuffer result = new InputBuffer(); + result.in = new ByteArrayInputStream(raw, offset, len); + return result; + } + + /** + * <p>Converts four octets into the number that they represent.</p> + * + * @param b the four octets. + * @return the length. + */ + // public static int fourBytesToLength(byte[] b) throws SaslEncodingException { + // int result = b[0] << 24 | (b[1] & 0xFF) << 16 | (b[2] & 0xFF) << 8 | (b[3] & 0xFF); + // if (result > Registry.SASL_FOUR_BYTE_MAX_LIMIT || result < 0) { + // throw new SaslEncodingException("SASL EOS size limit exceeded"); + // } + // return result; + // } + /** + * <p>Converts two octets into the number that they represent.</p> + * + * @param b the two octets. + * @return the length. + */ + public static int twoBytesToLength(byte[] b) throws SaslEncodingException + { + final int result = (b[0] & 0xFF) << 8 | (b[1] & 0xFF); + if (result > Registry.SASL_TWO_BYTE_MAX_LIMIT) + { + throw new SaslEncodingException("SASL MPI/Text size limit exceeded"); + } + return result; + } + + // Instance methods + // ------------------------------------------------------------------------- + + public boolean hasMoreElements() + { + return (in.available() > 0); + } + + /** + * <p>Decodes a SASL scalar quantity, <code>count</code>-octet long, from the + * current buffer.</p> + * + * @param count the number of octets of this scalar quantity. + * @return a native representation of a SASL scalar (unsigned integer) quantity. + * @throws SaslEncodingException if an encoding exception occurs during the + * operation. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public long getScalar(int count) throws IOException + { + if (count < 0 || count > 4) + { + throw new SaslEncodingException("Invalid SASL scalar octet count: " + + String.valueOf(count)); + } + if (!hasMoreElements()) + { + throw new SaslEncodingException( + "Not enough bytes for a scalar in buffer"); + } + if (in.available() < count) + { + throw new SaslEncodingException("Illegal SASL scalar encoding"); + } + byte[] element = new byte[count]; + in.read(element); + + long result = 0L; + for (int i = 0; i < count; i++) + { + result <<= 8; + result |= element[i] & 0xFFL; + } + return result; + } + + /** + * <p>Decodes a SASL OS from the current buffer.</p> + * + * @return a native representation of a SASL OS. + * @throws SaslEncodingException if an encoding exception occurs during the + * operation. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public byte[] getOS() throws IOException + { + if (!hasMoreElements()) + { + throw new SaslEncodingException( + "Not enough bytes for an octet-sequence in buffer"); + } + final int elementLength = in.read(); + if (elementLength > Registry.SASL_ONE_BYTE_MAX_LIMIT) + { + throw new SaslEncodingException( + "SASL octet-sequence size limit exceeded"); + } + + if (in.available() < elementLength) + { + throw new SaslEncodingException("Illegal SASL octet-sequence encoding"); + } + + byte[] result = new byte[elementLength]; + in.read(result); + + return result; + } + + /** + * <p>Decodes a SASL EOS from the current buffer.</p> + * + * @return a native representation of a SASL EOS. + * @throws SaslEncodingException if an encoding exception occurs during the + * operation. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public byte[] getEOS() throws IOException + { + if (in.available() < 2) + { + throw new SaslEncodingException( + "Not enough bytes for an extended octet-sequence in buffer"); + } + + byte[] elementLengthBytes = new byte[2]; + in.read(elementLengthBytes); + final int elementLength = twoBytesToLength(elementLengthBytes); + if (in.available() < elementLength) + { + throw new SaslEncodingException( + "Illegal SASL extended octet-sequence encoding"); + } + + byte[] result = new byte[elementLength]; + in.read(result); + + return result; + } + + /** + * <p>Decodes a SASL MPI from the current buffer.</p> + * + * @return a native representation of a SASL MPI. + * @throws SaslEncodingException if an encoding exception occurs during the + * operation. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public BigInteger getMPI() throws IOException + { + if (in.available() < 2) + { + throw new SaslEncodingException("Not enough bytes for an MPI in buffer"); + } + byte[] elementLengthBytes = new byte[2]; + in.read(elementLengthBytes); + final int elementLength = twoBytesToLength(elementLengthBytes); + if (in.available() < elementLength) + { + throw new SaslEncodingException( + "Illegal SASL multi-precision integer encoding"); + } + + byte[] element = new byte[elementLength]; + in.read(element); + + return new BigInteger(1, element); + } + + /** + * <p>Decodes a SASL Text from the current buffer.</p> + * + * @return a native representation of a SASL Text. + * @throws SaslEncodingException if an encoding exception occurs during the + * operation. + * @throws SaslEncodingException if the UTF-8 character encoding is not + * supported on this platform. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public String getText() throws IOException + { + if (in.available() < 2) + { + throw new SaslEncodingException("Not enough bytes for a text in buffer"); + } + byte[] elementLengthBytes = new byte[2]; + in.read(elementLengthBytes); + final int elementLength = twoBytesToLength(elementLengthBytes); + if (in.available() < elementLength) + { + throw new SaslEncodingException("Illegal SASL text encoding"); + } + + byte[] element = new byte[elementLength]; + in.read(element); + + return new String(element, "UTF8"); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/IntegrityException.java b/gnu/javax/crypto/sasl/IntegrityException.java new file mode 100644 index 000000000..4a56ca2d5 --- /dev/null +++ b/gnu/javax/crypto/sasl/IntegrityException.java @@ -0,0 +1,83 @@ +/* IntegrityException.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import javax.security.sasl.SaslException; + +/** + * Used by mechanisms that offer a security services layer, this checked + * exception is thrown to indicate that a violation has occured during the + * processing of an <i>integrity</i> protection filter, including <i>replay + * detection</i>. + */ +public class IntegrityException extends SaslException +{ + + /** + * Constructs a new instance of <code>IntegrityException</code> with no + * detail message. + */ + public IntegrityException() + { + super(); + } + + /** + * Constructs a new instance of <code>IntegrityException</code> with the + * specified detail message. + * + * @param s the detail message. + */ + public IntegrityException(String s) + { + super(s); + } + + /** + * Constructs a new instance of <code>IntegrityException</code> with a + * detailed message and a root exception. + * + * @param s possibly null additional detail about the exception. + * @param x a possibly null root exception that caused this one. + */ + public IntegrityException(String s, Throwable x) + { + super(s, x); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/NoSuchMechanismException.java b/gnu/javax/crypto/sasl/NoSuchMechanismException.java new file mode 100644 index 000000000..65432082a --- /dev/null +++ b/gnu/javax/crypto/sasl/NoSuchMechanismException.java @@ -0,0 +1,62 @@ +/* NoSuchMechanismException.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import javax.security.sasl.SaslException; + +/** + * A checked exception thrown to indicate that a designated SASL mechanism + * implementation was not found. + */ +public class NoSuchMechanismException extends SaslException +{ + + /** + * Constructs a <code>NoSuchMechanismException</code> with the specified + * detail message. In the case of this exception, the detail message + * designates the offending mechanism name. + * + * @param arg the detail message, which in this case is the offending + * mechanism name. + */ + public NoSuchMechanismException(String arg) + { + super(arg); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/NoSuchUserException.java b/gnu/javax/crypto/sasl/NoSuchUserException.java new file mode 100644 index 000000000..fe362c742 --- /dev/null +++ b/gnu/javax/crypto/sasl/NoSuchUserException.java @@ -0,0 +1,67 @@ +/* NoSuchUserException.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import javax.security.sasl.AuthenticationException; + +/** + * A checked exception thrown to indicate that a designated user is unknown to + * the authentication layer. + */ +public class NoSuchUserException extends AuthenticationException +{ + + /** Constructs a <code>NoSuchUserException</code> with no detail message. */ + public NoSuchUserException() + { + super(); + } + + /** + * Constructs a <code>NoSuchUserException</code> with the specified detail + * message. In the case of this exception, the detail message designates + * the offending username. + * + * @param arg the detail message, which in this case is the username. + */ + public NoSuchUserException(String arg) + { + super(arg); + } +} diff --git a/gnu/javax/crypto/sasl/OutputBuffer.java b/gnu/javax/crypto/sasl/OutputBuffer.java new file mode 100644 index 000000000..d219e7e9f --- /dev/null +++ b/gnu/javax/crypto/sasl/OutputBuffer.java @@ -0,0 +1,225 @@ +/* OutputBuffer.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.Registry; +import gnu.java.security.util.Util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; + +/** + * <p>The implementation of an outgoing SASL buffer.</p> + * + * <p>The data elements this class caters for are described in [1].</p> + * + * <p>References:</p> + * <ol> + * <li><a href="http://www.ietf.org/internet-drafts/draft-burdis-cat-srp-sasl-09.txt"> + * Secure Remote Password Authentication Mechanism</a>;<br/> + * draft-burdis-cat-srp-sasl-09,<br/> + * <a href="mailto:keith@rucus.ru.ac.za">Keith Burdis</a> and + * <a href="mailto:raif@forge.com.au">Raïf S. Naffah</a>.</li> + * </ol> + */ +public class OutputBuffer +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + /** The internal output stream. */ + private ByteArrayOutputStream out; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public OutputBuffer() + { + super(); + + out = new ByteArrayOutputStream(); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Encodes a SASL scalar quantity, <code>count</code>-octet long, to the + * current buffer.</p> + * + * @param count number of octets to encode <code>b</code> with. + * @param b the scalar quantity. + * @throws SaslEncodingException if an encoding size constraint is violated. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public void setScalar(int count, int b) throws IOException + { + if (count < 0 || count > 4) + { + throw new SaslEncodingException("Invalid SASL scalar octet count: " + + String.valueOf(count)); + } + byte[] element = new byte[count]; + for (int i = count; --i >= 0; b >>>= 8) + { + element[i] = (byte) b; + } + out.write(element); + } + + /** + * <p>Encodes a SASL OS to the current buffer.</p> + * + * @param b the OS element. + * @throws SaslEncodingException if an encoding size constraint is violated. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public void setOS(byte[] b) throws IOException + { + final int length = b.length; + if (length > Registry.SASL_ONE_BYTE_MAX_LIMIT) + { + throw new SaslEncodingException("SASL octet-sequence too long"); + } + out.write(length & 0xFF); + out.write(b); + } + + /** + * <p>Encodes a SASL EOS to the current buffer.</p> + * + * @param b the EOS element. + * @throws SaslEncodingException if an encoding size constraint is violated. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public void setEOS(byte[] b) throws IOException + { + final int length = b.length; + if (length > Registry.SASL_TWO_BYTE_MAX_LIMIT) + { + throw new SaslEncodingException("SASL extended octet-sequence too long"); + } + byte[] lengthBytes = { (byte) (length >>> 8), (byte) length }; + out.write(lengthBytes); + out.write(b); + } + + /** + * <p>Encodes a SASL MPI to the current buffer.</p> + * + * @param val the MPI element. + * @throws SaslEncodingException if an encoding size constraint is violated. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public void setMPI(BigInteger val) throws IOException + { + byte[] b = Util.trim(val); + final int length = b.length; + if (length > Registry.SASL_TWO_BYTE_MAX_LIMIT) + { + throw new SaslEncodingException("SASL multi-precision integer too long"); + } + byte[] lengthBytes = { (byte) (length >>> 8), (byte) length }; + out.write(lengthBytes); + out.write(b); + } + + /** + * <p>Encodes a SASL Text to the current buffer.</p> + * + * @param str the Text element. + * @throws SaslEncodingException if an encoding size constraint is violated. + * @throws SaslEncodingException if the UTF-8 encoding is not supported on + * this platform. + * @throws IOException if any other I/O exception occurs during the operation. + */ + public void setText(String str) throws IOException + { + byte[] b = str.getBytes("UTF8"); + final int length = b.length; + if (length > Registry.SASL_TWO_BYTE_MAX_LIMIT) + { + throw new SaslEncodingException("SASL text too long"); + } + byte[] lengthBytes = { (byte) (length >>> 8), (byte) length }; + out.write(lengthBytes); + out.write(b); + } + + /** + * <p>Returns the encoded form of the current buffer including the 4-byte + * length header.</p> + * + * @throws SaslEncodingException if an encoding size constraint is violated. + */ + public byte[] encode() throws SaslEncodingException + { + byte[] buffer = wrap(); + final int length = buffer.length; + byte[] result = new byte[length + 4]; + result[0] = (byte) (length >>> 24); + result[1] = (byte) (length >>> 16); + result[2] = (byte) (length >>> 8); + result[3] = (byte) length; + System.arraycopy(buffer, 0, result, 4, length); + + return result; + } + + /** + * <p>Returns the encoded form of the current buffer excluding the 4-byte + * length header.</p> + * + * @throws SaslEncodingException if an encoding size constraint is violated. + */ + public byte[] wrap() throws SaslEncodingException + { + final int length = out.size(); + if (length > Registry.SASL_BUFFER_MAX_LIMIT || length < 0) + { + throw new SaslEncodingException("SASL buffer too long"); + } + return out.toByteArray(); + } +} diff --git a/gnu/javax/crypto/sasl/SaslEncodingException.java b/gnu/javax/crypto/sasl/SaslEncodingException.java new file mode 100644 index 000000000..9f4c59f1c --- /dev/null +++ b/gnu/javax/crypto/sasl/SaslEncodingException.java @@ -0,0 +1,66 @@ +/* SaslEncodingException.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import javax.security.sasl.SaslException; + +/** + * A checked exception, thrown when an exception occurs while decoding a SASL + * buffer and/or a SASL data element from/to a buffer. + */ +public class SaslEncodingException extends SaslException +{ + + /** Constructs a <code>SaslEncodingException</code> with no detail message. */ + public SaslEncodingException() + { + super(); + } + + /** + * Constructs a <code>SaslEncodingException</code> with the specified detail + * message. + * + * @param s the detail message. + */ + public SaslEncodingException(String s) + { + super(s); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/SaslInputStream.java b/gnu/javax/crypto/sasl/SaslInputStream.java new file mode 100644 index 000000000..57eb2b5c5 --- /dev/null +++ b/gnu/javax/crypto/sasl/SaslInputStream.java @@ -0,0 +1,459 @@ +/* SaslInputStream.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.util.Util; + +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.IOException; +import java.io.PrintWriter; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslServer; + +/** + * An input stream that uses either a {@link SaslClient} or a {@link SaslServer} + * to process the data through these entities' security layer filter(s). + */ +public class SaslInputStream extends InputStream +{ + + // Debugging methods and variables + // ------------------------------------------------------------------------- + + private static final String NAME = "SaslOutputStream"; + + private static final String ERROR = "ERROR"; + + private static final String WARN = " WARN"; + + // private static final String INFO = " INFO"; + private static final String TRACE = "DEBUG"; + + private static final boolean DEBUG = true; + + private static final int debuglevel = 3; + + private static final PrintWriter err = new PrintWriter(System.out, true); + + private static void debug(String level, Object obj) + { + err.println("[" + level + "] " + NAME + ": " + String.valueOf(obj)); + } + + // Constants and variables + // ------------------------------------------------------------------------- + + private SaslClient client; + + private SaslServer server; + + private int maxRawSendSize; + + private InputStream source; + + private byte[] internalBuf; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public SaslInputStream(SaslClient client, InputStream source) + throws IOException + { + super(); + + this.client = client; + maxRawSendSize = Integer.parseInt((String) client.getNegotiatedProperty(Sasl.RAW_SEND_SIZE)); + server = null; + this.source = source; + } + + public SaslInputStream(SaslServer server, InputStream source) + throws IOException + { + super(); + + this.server = server; + maxRawSendSize = Integer.parseInt((String) server.getNegotiatedProperty(Sasl.RAW_SEND_SIZE)); + client = null; + this.source = source; + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // Overloaded java.io.InputStream methods ---------------------------------- + + public int available() throws IOException + { + return (internalBuf == null) ? 0 : internalBuf.length; + } + + public void close() throws IOException + { + source.close(); + } + + /** + * <p>Reads the next byte of data from the input stream. The value byte is + * returned as an <code>int</code> in the range <code>0</code> to + * <code>255</code>. If no byte is available because the end of the stream + * has been reached, the value <code>-1</code> is returned. This method + * blocks until input data is available, the end of the stream is detected, + * or an exception is thrown.</p> + * + * <p>From a SASL mechanism provider's perspective, if a security layer has + * been negotiated, the underlying <i>source</i> is expected to contain SASL + * buffers, as defined in RFC 2222. Four octets in network byte order in the + * front of each buffer identify the length of the buffer. The provider is + * responsible for performing any integrity checking or other processing on + * the buffer before returning the data as a stream of octets. For example, + * the protocol driver's request for a single octet from the stream might; + * i.e. an invocation of this method, may result in an entire SASL buffer + * being read and processed before that single octet can be returned.</p> + * + * @return the next byte of data, or <code>-1</code> if the end of the stream + * is reached. + * @throws IOException if an I/O error occurs. + */ + public int read() throws IOException + { + int result = -1; + if (internalBuf != null && internalBuf.length > 0) + { + result = internalBuf[0] & 0xFF; + if (internalBuf.length == 1) + internalBuf = new byte[0]; + else + { + byte[] tmp = new byte[internalBuf.length - 1]; + // System.arraycopy(internalBuf, 0, tmp, 0, tmp.length); + System.arraycopy(internalBuf, 1, tmp, 0, tmp.length); + internalBuf = tmp; + } + } + else + { + byte[] buf = new byte[1]; + int check = read(buf); + result = (check > 0) ? (buf[0] & 0xFF) : -1; + } + + return result; + } + + /** + * <p>Reads up to <code>len</code> bytes of data from the underlying + * <i>source</i> input stream into an array of bytes. An attempt is made to + * read as many as <code>len</code> bytes, but a smaller number may be read, + * possibly zero. The number of bytes actually read is returned as an + * integer.</p> + * + * <p>This method blocks until input data is available, end of file is + * detected, or an exception is thrown.</p> + * + * <p>If <code>b</code> is <code>null</code>, a {@link NullPointerException} is + * thrown.</p> + * + * <p>If <code>off</code> is negative, or <code>len</code> is negative, or + * <code>off+len</code> is greater than the length of the array <code>b</code>, + * then an {@link IndexOutOfBoundsException} is thrown.</p> + * + * <p>If <code>len</code> is zero, then no bytes are read and <code>0</code> + * is returned; otherwise, there is an attempt to read at least one byte. If + * no byte is available because the stream is at end of file, the value + * <code>-1</code> is returned; otherwise, at least one byte is read and + * stored into <code>b</code>.</p> + * + * <p>The first byte read is stored into element <code>b[off]</code>, the + * next one into <code>b[off+1]</code>, and so on. The number of bytes read + * is, at most, equal to <code>len</code>. Let <code>k</code> be the number + * of bytes actually read; these bytes will be stored in elements + * <code>b[off]</code> through <code>b[off+k-1]</code>, leaving elements + * <code>b[off+k]</code> through <code>b[off+len-1]</code> unaffected.</p> + * + * <p>In every case, elements <code>b[0]</code> through <code>b[off]</code> + * and elements <code>b[off+len]</code> through <code>b[b.length-1]</code> + * are unaffected.</p> + * + * <p>If the first byte cannot be read for any reason other than end of file, + * then an {@link IOException} is thrown. In particular, an {@link IOException} + * is thrown if the input stream has been closed.</p> + * + * <p>From the SASL mechanism provider's perspective, if a security layer has + * been negotiated, the underlying <i>source</i> is expected to contain SASL + * buffers, as defined in RFC 2222. Four octets in network byte order in the + * front of each buffer identify the length of the buffer. The provider is + * responsible for performing any integrity checking or other processing on + * the buffer before returning the data as a stream of octets. The protocol + * driver's request for a single octet from the stream might result in an + * entire SASL buffer being read and processed before that single octet can + * be returned.</p> + * + * @param b the buffer into which the data is read. + * @param off the start offset in array <code>b</code> at which the data is + * wricodeen. + * @param len the maximum number of bytes to read. + * @return the total number of bytes read into the buffer, or <code>-1</code> + * if there is no more data because the end of the stream has been reached. + * @throws IOException if an I/O error occurs. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> read(b, " + String.valueOf(off) + ", " + + String.valueOf(len) + ")"); + + if (b == null) + { + throw new NullPointerException("b"); + } + if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) + || ((off + len) < 0)) + { + throw new IndexOutOfBoundsException("off=" + String.valueOf(off) + + ", len=" + String.valueOf(len) + + ", b.length=" + + String.valueOf(b.length)); + } + if (len == 0) + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== read() --> 0"); + return 0; + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Available: " + String.valueOf(available())); + + int result = 0; + if (internalBuf == null || internalBuf.length < 1) + try + { + internalBuf = readSaslBuffer(); + if (internalBuf == null) + { + if (DEBUG && debuglevel > 4) + debug(WARN, "Underlying stream empty. Returning -1"); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== read() --> -1"); + return -1; + } + } + catch (InterruptedIOException x) + { + if (DEBUG && debuglevel > 6) + debug(TRACE, x); + if (DEBUG && debuglevel > 4) + debug(WARN, "Reading thread was interrupted. Returning -1"); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== read() --> -1"); + return -1; + } + + if (len <= internalBuf.length) + { + result = len; + System.arraycopy(internalBuf, 0, b, off, len); + if (len == internalBuf.length) + internalBuf = null; + else + { + byte[] tmp = new byte[internalBuf.length - len]; + System.arraycopy(internalBuf, len, tmp, 0, tmp.length); + internalBuf = tmp; + } + } + else + { + // first copy the available bytes to b + result = internalBuf.length; + System.arraycopy(internalBuf, 0, b, off, result); + internalBuf = null; + + off += result; + len -= result; + + int remaining; // count of bytes remaining in buffer after an iteration + int delta; // count of bytes moved to b after an iteration + int datalen; + byte[] data; + while (len > 0) + // we need to read SASL buffers, as long as there are at least + // 4 bytes available at the source + if (source.available() > 3) + { + // process a buffer + data = readSaslBuffer(); + if (data == null) + { + if (DEBUG && debuglevel > 4) + debug(WARN, "Underlying stream exhausted. Breaking..."); + break; + } + + datalen = data.length; + + // copy [part of] the result to b + remaining = (datalen <= len) ? 0 : datalen - len; + delta = datalen - remaining; + System.arraycopy(data, 0, b, off, delta); + if (remaining > 0) + { + internalBuf = new byte[remaining]; + System.arraycopy(data, delta, internalBuf, 0, remaining); + } + + // update off, result and len + off += delta; + result += delta; + len -= delta; + } + else + { // nothing much we can do except return what we have + if (DEBUG && debuglevel > 4) + debug(WARN, + "Not enough bytes in source to read a buffer. Breaking..."); + break; + } + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Remaining: " + + (internalBuf == null ? 0 : internalBuf.length)); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== read() --> " + String.valueOf(result)); + return result; + } + + // other nstance methods --------------------------------------------------- + + /** + * Reads a SASL buffer from the underlying source if at least 4 bytes are + * available. + * + * @return the byte[] of decoded buffer contents, or null if the underlying + * source was exhausted. + * @throws IOException if an I/O exception occurs during the operation. + */ + private byte[] readSaslBuffer() throws IOException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> readSaslBuffer()"); + + int realLength; // check if we read as many bytes as we're supposed to + byte[] result = new byte[4]; + try + { + realLength = source.read(result); + if (realLength == -1) + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== readSaslBuffer() --> null"); + return null; + } + } + catch (IOException x) + { + if (DEBUG && debuglevel > 0) + debug(ERROR, x); + throw x; + } + + if (realLength != 4) + { + throw new IOException("Was expecting 4 but found " + + String.valueOf(realLength)); + } + int bufferLength = result[0] << 24 | (result[1] & 0xFF) << 16 + | (result[2] & 0xFF) << 8 | (result[3] & 0xFF); + + if (DEBUG && debuglevel > 6) + debug(TRACE, "SASL buffer size: " + bufferLength); + if (bufferLength > maxRawSendSize || bufferLength < 0) + { + throw new SaslEncodingException("SASL buffer (security layer) too long"); + } + + result = new byte[bufferLength]; + try + { + realLength = source.read(result); + } + catch (IOException x) + { + if (DEBUG && debuglevel > 0) + debug(ERROR, x); + throw x; + } + + if (realLength != bufferLength) + throw new IOException("Was expecting " + String.valueOf(bufferLength) + + " but found " + String.valueOf(realLength)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Incoming buffer (before security) (hex): " + + Util.dumpString(result)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Incoming buffer (before security) (str): \"" + + new String(result) + "\""); + + if (client != null) + { + result = client.unwrap(result, 0, realLength); + } + else + { + result = server.unwrap(result, 0, realLength); + } + if (DEBUG && debuglevel > 6) + debug(TRACE, "Incoming buffer (after security) (hex): " + + Util.dumpString(result)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Incoming buffer (after security) (str): \"" + + new String(result) + "\""); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== readSaslBuffer()"); + return result; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/SaslOutputStream.java b/gnu/javax/crypto/sasl/SaslOutputStream.java new file mode 100644 index 000000000..699720137 --- /dev/null +++ b/gnu/javax/crypto/sasl/SaslOutputStream.java @@ -0,0 +1,218 @@ +/* SaslOutputStream.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.util.Util; + +import java.io.OutputStream; +import java.io.IOException; +import java.io.PrintWriter; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslServer; + +/** + * An output stream that uses either a {@link SaslClient} or a {@link SaslServer} + * to process the data through these entities' security layer filter(s). + */ +public class SaslOutputStream extends OutputStream +{ + + // Debugging methods and variables + // ------------------------------------------------------------------------- + + private static final String NAME = "SaslOutputStream"; + + // private static final String ERROR = "ERROR"; + // private static final String WARN = " WARN"; + // private static final String INFO = " INFO"; + private static final String TRACE = "DEBUG"; + + private static final boolean DEBUG = true; + + private static final int debuglevel = 3; + + private static final PrintWriter err = new PrintWriter(System.out, true); + + private static void debug(String level, Object obj) + { + err.println("[" + level + "] " + NAME + ": " + String.valueOf(obj)); + } + + // Constants and variables + // ------------------------------------------------------------------------- + + private SaslClient client; + + private SaslServer server; + + private int maxRawSendSize; + + private OutputStream dest; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public SaslOutputStream(SaslClient client, OutputStream dest) + throws IOException + { + super(); + + this.client = client; + maxRawSendSize = Integer.parseInt((String) client.getNegotiatedProperty(Sasl.RAW_SEND_SIZE)); + server = null; + this.dest = dest; + } + + public SaslOutputStream(SaslServer server, OutputStream dest) + throws IOException + { + super(); + + this.server = server; + maxRawSendSize = Integer.parseInt((String) server.getNegotiatedProperty(Sasl.RAW_SEND_SIZE)); + client = null; + this.dest = dest; + } + + // Class methods + // ------------------------------------------------------------------------- + + // Overloaded java.io.OutputStream methods + // ------------------------------------------------------------------------- + + public void close() throws IOException + { + dest.flush(); + dest.close(); + } + + public void flush() throws IOException + { + dest.flush(); + } + + /** + * When writing octets to the resulting stream, if a security layer has been + * negotiated, each piece of data written (by a single invocation of + * <code>write()</code>) will be encapsulated as a SASL buffer, as defined in + * RFC 2222, and then written to the underlying <i>dest</i> output stream. + */ + public void write(int b) throws IOException + { + write(new byte[] { (byte) b }); + } + + /** + * When writing octets to the resulting stream, if a security layer has been + * negotiated, each piece of data written (by a single invocation of + * <code>write()</code>) will be encapsulated as a SASL buffer, as defined in + * RFC 2222, and then written to the underlying <i>dest</i> output stream. + */ + public void write(byte[] b, int off, int len) throws IOException + { + if (b == null) + { + throw new NullPointerException("b"); + } + if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) + || ((off + len) < 0)) + { + throw new IndexOutOfBoundsException("off=" + String.valueOf(off) + + ", len=" + String.valueOf(len) + + ", b.length=" + + String.valueOf(b.length)); + } + if (len == 0) + { + return; + } + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> write()"); + + int chunckSize, length, chunck = 1; + byte[] output = null, result; + if (DEBUG && debuglevel > 6) + debug(TRACE, "About to wrap " + String.valueOf(len) + " byte(s)..."); + while (len > 0) + { + chunckSize = (len > maxRawSendSize ? maxRawSendSize : len); + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Outgoing buffer (before security) (hex): " + + Util.dumpString(b, off, chunckSize)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Outgoing buffer (before security) (str): \"" + + new String(b, off, chunckSize) + "\""); + + if (client != null) + output = client.wrap(b, off, chunckSize); + else + output = server.wrap(b, off, chunckSize); + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Outgoing buffer (after security) (hex): " + + Util.dumpString(output)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Outgoing buffer (after security) (str): \"" + + new String(output) + "\""); + + length = output.length; + result = new byte[length + 4]; + result[0] = (byte) (length >>> 24); + result[1] = (byte) (length >>> 16); + result[2] = (byte) (length >>> 8); + result[3] = (byte) length; + System.arraycopy(output, 0, result, 4, length); + + dest.write(result); + + off += chunckSize; + len -= chunckSize; + if (DEBUG && debuglevel > 6) + debug(TRACE, "Wrapped chunck #" + String.valueOf(chunck)); + chunck++; + } + + dest.flush(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== write()"); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/SaslUtil.java b/gnu/javax/crypto/sasl/SaslUtil.java new file mode 100644 index 000000000..e70312c0d --- /dev/null +++ b/gnu/javax/crypto/sasl/SaslUtil.java @@ -0,0 +1,89 @@ +/* SaslUtil.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.util.Util; + +import java.math.BigInteger; +import java.security.MessageDigest; + +/** + * Utility methods for SASL-related classes. + */ +public class SaslUtil +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + private SaslUtil() + { + super(); + } + + // Class methods + // ------------------------------------------------------------------------- + + public static final boolean validEmailAddress(String address) + { + // need to do better than this + return (address.indexOf("@") != -1); + } + + // Visualisation methods + // ------------------------------------------------------------------------- + + /** Returns the context of the designated hash as a string. */ + public static final String dump(MessageDigest md) + { + String result; + try + { + result = Util.dumpString(((MessageDigest) md.clone()).digest()); + } + catch (Exception ignored) + { + result = "..."; + } + return result; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/ServerFactory.java b/gnu/javax/crypto/sasl/ServerFactory.java new file mode 100644 index 000000000..e9b08dbd4 --- /dev/null +++ b/gnu/javax/crypto/sasl/ServerFactory.java @@ -0,0 +1,194 @@ +/* ServerFactory.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.anonymous.AnonymousServer; +import gnu.javax.crypto.sasl.crammd5.CramMD5Server; +import gnu.javax.crypto.sasl.plain.PlainServer; +import gnu.javax.crypto.sasl.srp.SRPServer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslServerFactory; + +/** + * The implementation of the {@link SaslServerFactory}. + */ +public class ServerFactory implements SaslServerFactory +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + // implicit 0-arguments constructor + + // Class methods + // ------------------------------------------------------------------------- + + public static final Set getNames() + { + return Collections.unmodifiableSet(new HashSet( + Arrays.asList(getNamesInternal(null)))); + } + + private static final String[] getNamesInternal(Map props) + { + String[] all = new String[] { Registry.SASL_SRP_MECHANISM, + Registry.SASL_CRAM_MD5_MECHANISM, + Registry.SASL_PLAIN_MECHANISM, + Registry.SASL_ANONYMOUS_MECHANISM }; + + List result = new ArrayList(4); + int i; + for (i = 0; i < all.length;) + { + result.add(all[i++]); + } + + if (props == null) + { + return (String[]) result.toArray(new String[0]); // all + } + if (hasPolicy(Sasl.POLICY_PASS_CREDENTIALS, props)) + { // none + return new String[0]; + } + + if (hasPolicy(Sasl.POLICY_NOPLAINTEXT, props)) + { + result.remove(Registry.SASL_PLAIN_MECHANISM); + } + if (hasPolicy(Sasl.POLICY_NOACTIVE, props)) + { + result.remove(Registry.SASL_CRAM_MD5_MECHANISM); + result.remove(Registry.SASL_PLAIN_MECHANISM); + } + if (hasPolicy(Sasl.POLICY_NODICTIONARY, props)) + { + result.remove(Registry.SASL_CRAM_MD5_MECHANISM); + result.remove(Registry.SASL_PLAIN_MECHANISM); + } + if (hasPolicy(Sasl.POLICY_NOANONYMOUS, props)) + { + result.remove(Registry.SASL_ANONYMOUS_MECHANISM); + } + if (hasPolicy(Sasl.POLICY_FORWARD_SECRECY, props)) + { + result.remove(Registry.SASL_CRAM_MD5_MECHANISM); + result.remove(Registry.SASL_ANONYMOUS_MECHANISM); + result.remove(Registry.SASL_PLAIN_MECHANISM); + } + return (String[]) result.toArray(new String[0]); + } + + public static final ServerMechanism getInstance(String mechanism) + { + if (mechanism == null) + { + return null; + } + mechanism = mechanism.trim().toUpperCase(); + if (mechanism.equals(Registry.SASL_SRP_MECHANISM)) + { + return new SRPServer(); + } + if (mechanism.equals(Registry.SASL_CRAM_MD5_MECHANISM)) + { + return new CramMD5Server(); + } + if (mechanism.equals(Registry.SASL_PLAIN_MECHANISM)) + { + return new PlainServer(); + } + if (mechanism.equals(Registry.SASL_ANONYMOUS_MECHANISM)) + { + return new AnonymousServer(); + } + return null; + } + + // Instance methods + // ------------------------------------------------------------------------- + + public SaslServer createSaslServer(String mechanism, String protocol, + String serverName, Map props, + CallbackHandler cbh) throws SaslException + { + ServerMechanism result = getInstance(mechanism); + if (result != null) + { + HashMap attributes = new HashMap(); + if (props != null) + { + attributes.putAll(props); + } + attributes.put(Registry.SASL_PROTOCOL, protocol); + attributes.put(Registry.SASL_SERVER_NAME, serverName); + attributes.put(Registry.SASL_CALLBACK_HANDLER, cbh); + + result.init(attributes); + } + return result; + } + + public String[] getMechanismNames(Map props) + { + return getNamesInternal(props); + } + + private static boolean hasPolicy(String propertyName, Map props) + { + return "true".equalsIgnoreCase(String.valueOf(props.get(propertyName))); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/ServerMechanism.java b/gnu/javax/crypto/sasl/ServerMechanism.java new file mode 100644 index 000000000..f12a075d9 --- /dev/null +++ b/gnu/javax/crypto/sasl/ServerMechanism.java @@ -0,0 +1,371 @@ +/* ServerMechanism.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import gnu.java.security.Registry; + +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslException; + +/** + * <p>A base class to facilitate implementing SASL server-side mechanisms.</p> + */ +public abstract class ServerMechanism implements SaslServer +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + /** Name of this mechanism. */ + protected String mechanism; + + /** Name of protocol using this mechanism. */ + protected String protocol; + + /** Name of server to authenticate to. */ + protected String serverName; + + /** Properties of qualities desired for this mechanism. */ + protected Map properties; + + /** Callback handler to use with this mechanism instance. */ + protected CallbackHandler handler; + + /** Whether authentication phase is completed (true) or not (false). */ + protected boolean complete = false; + + /** The authorisation identity. */ + protected String authorizationID; + + /** Channel binding data to use with this mechanism instance. */ + protected byte[] channelBinding; + + /** The state of the authentication automaton. -1 means uninitialised. */ + protected int state = -1; + + /** The provider for authentication information. */ + protected IAuthInfoProvider authenticator; + + // Constructor(s) + // ------------------------------------------------------------------------- + + protected ServerMechanism(final String mechanism) + { + super(); + + this.mechanism = mechanism; + this.authenticator = AuthInfo.getProvider(mechanism); + this.state = -1; + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods to be implemented by concrete subclasses --------------- + + protected abstract void initMechanism() throws SaslException; + + protected abstract void resetMechanism() throws SaslException; + + // javax.security.sasl.SaslServer interface implementation ----------------- + + public abstract byte[] evaluateResponse(byte[] response) throws SaslException; + + public boolean isComplete() + { + return complete; + } + + public byte[] unwrap(final byte[] incoming, final int offset, final int len) + throws SaslException + { + if (!isComplete()) + { + throw new IllegalMechanismStateException(); + } + return this.engineUnwrap(incoming, offset, len); + } + + public byte[] wrap(final byte[] outgoing, final int offset, final int len) + throws SaslException + { + if (!isComplete()) + { + throw new IllegalMechanismStateException(); + } + return this.engineWrap(outgoing, offset, len); + } + + public String getMechanismName() + { + return this.mechanism; + } + + public String getAuthorizationID() + { + return this.authorizationID; + } + + public Object getNegotiatedProperty(final String propName) + { + if (!isComplete()) + { + throw new IllegalStateException(); + } + if (Sasl.QOP.equals(propName)) + { + return getNegotiatedQOP(); + } + if (Sasl.STRENGTH.equals(propName)) + { + return getNegotiatedStrength(); + } + if (Sasl.SERVER_AUTH.equals(propName)) + { + return getNegotiatedServerAuth(); + } + if (Sasl.MAX_BUFFER.equals(propName)) + { + return getNegotiatedMaxBuffer(); + } + if (Sasl.RAW_SEND_SIZE.equals(propName)) + { + return getNegotiatedRawSendSize(); + } + if (Sasl.POLICY_NOPLAINTEXT.equals(propName)) + { + return getNegotiatedPolicyNoPlainText(); + } + if (Sasl.POLICY_NOACTIVE.equals(propName)) + { + return getNegotiatedPolicyNoActive(); + } + if (Sasl.POLICY_NODICTIONARY.equals(propName)) + { + return getNegotiatedPolicyNoDictionary(); + } + if (Sasl.POLICY_NOANONYMOUS.equals(propName)) + { + return getNegotiatedPolicyNoAnonymous(); + } + if (Sasl.POLICY_FORWARD_SECRECY.equals(propName)) + { + return getNegotiatedPolicyForwardSecrecy(); + } + if (Sasl.POLICY_PASS_CREDENTIALS.equals(propName)) + { + return getNegotiatedPolicyPassCredentials(); + } + if (Sasl.REUSE.equals(propName)) + { + return getReuse(); + } + return null; + } + + public void dispose() throws SaslException + { + reset(); + } + + // other Instance methods -------------------------------------------------- + + protected String getNegotiatedQOP() + { + return Registry.QOP_AUTH; + } + + protected String getNegotiatedStrength() + { + return Registry.STRENGTH_LOW; + } + + protected String getNegotiatedServerAuth() + { + return Registry.SERVER_AUTH_FALSE; + } + + protected String getNegotiatedMaxBuffer() + { + return null; + } + + protected String getNegotiatedPolicyNoPlainText() + { + return null; + } + + protected String getNegotiatedPolicyNoActive() + { + return null; + } + + protected String getNegotiatedPolicyNoDictionary() + { + return null; + } + + protected String getNegotiatedPolicyNoAnonymous() + { + return null; + } + + protected String getNegotiatedPolicyForwardSecrecy() + { + return null; + } + + protected String getNegotiatedPolicyPassCredentials() + { + return null; + } + + protected String getNegotiatedRawSendSize() + { + return String.valueOf(Registry.SASL_BUFFER_MAX_LIMIT); + } + + protected String getReuse() + { + return Registry.REUSE_FALSE; + } + + protected byte[] engineUnwrap(final byte[] incoming, final int offset, + final int len) throws SaslException + { + final byte[] result = new byte[len]; + System.arraycopy(incoming, offset, result, 0, len); + return result; + } + + protected byte[] engineWrap(final byte[] outgoing, final int offset, + final int len) throws SaslException + { + final byte[] result = new byte[len]; + System.arraycopy(outgoing, offset, result, 0, len); + return result; + } + + /** + * <p>Initialises the mechanism with designated attributes. Permissible names + * and values are mechanism specific.</p> + * + * @param attributes a set of name-value pairs that describes the desired + * future behaviour of this instance. + * @throws IllegalMechanismStateException if the instance is already + * initialised. + * @throws SaslException if an exception occurs during the process. + */ + public void init(final Map attributes) throws SaslException + { + if (state != -1) + { + throw new IllegalMechanismStateException("init()"); + } + + if (properties == null) + { + properties = new HashMap(); + } + else + { + properties.clear(); + } + if (attributes != null) + { + protocol = (String) attributes.get(Registry.SASL_PROTOCOL); + serverName = (String) attributes.get(Registry.SASL_SERVER_NAME); + handler = (CallbackHandler) attributes.get(Registry.SASL_CALLBACK_HANDLER); + channelBinding = (byte[]) attributes.get(Registry.SASL_CHANNEL_BINDING); + properties.putAll(attributes); + } + else + { + handler = null; + } + + if (protocol == null) + { + protocol = ""; + } + if (serverName == null) + { + serverName = ""; + } + if (authenticator != null) + { + authenticator.activate(properties); + } + if (channelBinding == null) + { + channelBinding = new byte[0]; + } + initMechanism(); + complete = false; + state = 0; + } + + /** + * <p>Resets the mechanism instance for re-initialisation and use with other + * characteristics.</p> + * + * @throws SaslException if an exception occurs during the process. + */ + public void reset() throws SaslException + { + resetMechanism(); + properties.clear(); + if (authenticator != null) + { + authenticator.passivate(); + } + protocol = serverName = null; + channelBinding = null; + complete = false; + state = -1; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/UserAlreadyExistsException.java b/gnu/javax/crypto/sasl/UserAlreadyExistsException.java new file mode 100644 index 000000000..764a36df3 --- /dev/null +++ b/gnu/javax/crypto/sasl/UserAlreadyExistsException.java @@ -0,0 +1,70 @@ +/* UserAlreadyExistsException.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl; + +import javax.security.sasl.SaslException; + +/** + * A checked exception thrown to indicate that a designated user is already + * known to the the authentication layer. + */ +public class UserAlreadyExistsException extends SaslException +{ + + /** + * Constructs a <code>UserAlreadyExistsException</code> with no detail + * message. + */ + public UserAlreadyExistsException() + { + super(); + } + + /** + * Constructs a <code>UserAlreadyExistsException</code> with the specified + * detail message. In the case of this exception, the detail message + * designates the offending username. + * + * @param userName the detail message, which in this case is the username. + */ + public UserAlreadyExistsException(String userName) + { + super(userName); + } +} diff --git a/gnu/javax/crypto/sasl/anonymous/AnonymousClient.java b/gnu/javax/crypto/sasl/anonymous/AnonymousClient.java new file mode 100644 index 000000000..f5b1faab2 --- /dev/null +++ b/gnu/javax/crypto/sasl/anonymous/AnonymousClient.java @@ -0,0 +1,120 @@ +/* AnonymousClient.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.anonymous; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.ClientMechanism; +import gnu.javax.crypto.sasl.IllegalMechanismStateException; + +import java.io.UnsupportedEncodingException; + +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.sasl.AuthenticationException; + +/** + * <p>The ANONYMOUS client-side mechanism.</p> + */ +public class AnonymousClient extends ClientMechanism implements SaslClient +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + public AnonymousClient() + { + super(Registry.SASL_ANONYMOUS_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + } + + protected void resetMechanism() throws SaslException + { + } + + // javax.security.sasl.SaslClient interface implementation ----------------- + + public boolean hasInitialResponse() + { + return true; + } + + public byte[] evaluateChallenge(final byte[] challenge) throws SaslException + { + if (complete) + { + throw new IllegalMechanismStateException("evaluateChallenge()"); + } + return response(); + } + + private byte[] response() throws SaslException + { + if (!AnonymousUtil.isValidTraceInformation(authorizationID)) + { + throw new AuthenticationException( + "Authorisation ID is not a valid email address"); + } + complete = true; + // return authorizationID.getBytes(); + final byte[] result; + try + { + result = authorizationID.getBytes("UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("response()", x); + } + return result; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/anonymous/AnonymousServer.java b/gnu/javax/crypto/sasl/anonymous/AnonymousServer.java new file mode 100644 index 000000000..2c10f78a7 --- /dev/null +++ b/gnu/javax/crypto/sasl/anonymous/AnonymousServer.java @@ -0,0 +1,107 @@ +/* AnonymousServer.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.anonymous; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.ServerMechanism; + +import java.io.UnsupportedEncodingException; + +import javax.security.sasl.AuthenticationException; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +/** + * The ANONYMOUS server-side mechanism. + */ +public class AnonymousServer extends ServerMechanism implements SaslServer +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + public AnonymousServer() + { + super(Registry.SASL_ANONYMOUS_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + } + + protected void resetMechanism() throws SaslException + { + } + + // javax.security.sasl.SaslServer interface implementation ----------------- + + public byte[] evaluateResponse(final byte[] response) throws SaslException + { + if (response == null) + { + return null; + } + try + { + authorizationID = new String(response, "UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("evaluateResponse()", x); + } + if (AnonymousUtil.isValidTraceInformation(authorizationID)) + { + this.complete = true; + return null; + } + authorizationID = null; + throw new AuthenticationException("Invalid email address"); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/anonymous/AnonymousUtil.java b/gnu/javax/crypto/sasl/anonymous/AnonymousUtil.java new file mode 100644 index 000000000..99e95eaec --- /dev/null +++ b/gnu/javax/crypto/sasl/anonymous/AnonymousUtil.java @@ -0,0 +1,109 @@ +/* AnonymousUtil.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.anonymous; + +import gnu.javax.crypto.sasl.SaslUtil; + +/** + * An ANONYMOUS-specific utility class. + */ +public class AnonymousUtil +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** Trivial private constructor to enforce Singleton pattern. */ + private AnonymousUtil() + { + super(); + } + + // Class methods + // ------------------------------------------------------------------------- + + static boolean isValidTraceInformation(String traceInformation) + { + if (traceInformation == null) + { + return false; + } + if (traceInformation.length() == 0) + { + return true; + } + if (SaslUtil.validEmailAddress(traceInformation)) + { + return true; + } + return isValidToken(traceInformation); + } + + static boolean isValidToken(String token) + { + if (token == null) + { + return false; + } + if (token.length() == 0) + { + return false; + } + if (token.length() > 255) + { + return false; + } + if (token.indexOf('@') != -1) + { + return false; + } + for (int i = 0; i < token.length(); i++) + { + char c = token.charAt(i); + if (c < 0x20 || c > 0x7E) + { + return false; + } + } + return true; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/crammd5/CramMD5AuthInfoProvider.java b/gnu/javax/crypto/sasl/crammd5/CramMD5AuthInfoProvider.java new file mode 100644 index 000000000..cf73b6f98 --- /dev/null +++ b/gnu/javax/crypto/sasl/crammd5/CramMD5AuthInfoProvider.java @@ -0,0 +1,200 @@ +/* CramMD5AuthInfoProvider.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.crammd5; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.IAuthInfoProvider; +import gnu.javax.crypto.sasl.NoSuchUserException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.security.sasl.AuthenticationException; + +/** + * The CRAM-MD5 mechanism authentication information provider implementation. + */ +public class CramMD5AuthInfoProvider implements IAuthInfoProvider +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private PasswordFile passwordFile = null; + + // Constructor(s) + // ------------------------------------------------------------------------- + + // implicit 0-args constrcutor + + // Class methods + // ------------------------------------------------------------------------- + + // IAuthInfoProvider interface implementation + // ------------------------------------------------------------------------- + + public void activate(Map context) throws AuthenticationException + { + try + { + if (context == null) + { + passwordFile = new PasswordFile(); + } + else + { + String pfn = (String) context.get(CramMD5Registry.PASSWORD_FILE); + if (pfn == null) + { + passwordFile = new PasswordFile(); + } + else + { + passwordFile = new PasswordFile(pfn); + } + } + } + catch (IOException x) + { + throw new AuthenticationException("activate()", x); + } + } + + public void passivate() throws AuthenticationException + { + passwordFile = null; + } + + public boolean contains(String userName) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("contains()", + new IllegalStateException()); + } + boolean result = false; + try + { + result = passwordFile.contains(userName); + } + catch (IOException x) + { + throw new AuthenticationException("contains()", x); + } + return result; + } + + public Map lookup(Map userID) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("lookup()", + new IllegalStateException()); + } + Map result = new HashMap(); + try + { + String userName = (String) userID.get(Registry.SASL_USERNAME); + if (userName == null) + { + throw new NoSuchUserException(""); + } + String[] data = passwordFile.lookup(userName); + result.put(Registry.SASL_USERNAME, data[0]); + result.put(Registry.SASL_PASSWORD, data[1]); + result.put(CramMD5Registry.UID_FIELD, data[2]); + result.put(CramMD5Registry.GID_FIELD, data[3]); + result.put(CramMD5Registry.GECOS_FIELD, data[4]); + result.put(CramMD5Registry.DIR_FIELD, data[5]); + result.put(CramMD5Registry.SHELL_FIELD, data[6]); + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + throw new AuthenticationException("lookup()", x); + } + return result; + } + + public void update(Map userCredentials) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("update()", + new IllegalStateException()); + } + try + { + String userName = (String) userCredentials.get(Registry.SASL_USERNAME); + String password = (String) userCredentials.get(Registry.SASL_PASSWORD); + String uid = (String) userCredentials.get(CramMD5Registry.UID_FIELD); + String gid = (String) userCredentials.get(CramMD5Registry.GID_FIELD); + String gecos = (String) userCredentials.get(CramMD5Registry.GECOS_FIELD); + String dir = (String) userCredentials.get(CramMD5Registry.DIR_FIELD); + String shell = (String) userCredentials.get(CramMD5Registry.SHELL_FIELD); + if (uid == null || gid == null || gecos == null || dir == null + || shell == null) + { + passwordFile.changePasswd(userName, password); + } + else + { + String[] attributes = new String[] { uid, gid, gecos, dir, shell }; + passwordFile.add(userName, password, attributes); + } + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + throw new AuthenticationException("update()", x); + } + } + + public Map getConfiguration(String mode) throws AuthenticationException + { + throw new AuthenticationException("", new UnsupportedOperationException()); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/crammd5/CramMD5Client.java b/gnu/javax/crypto/sasl/crammd5/CramMD5Client.java new file mode 100644 index 000000000..094109ff9 --- /dev/null +++ b/gnu/javax/crypto/sasl/crammd5/CramMD5Client.java @@ -0,0 +1,201 @@ +/* CramMD5Client.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.crammd5; + +import gnu.java.security.Registry; +import gnu.java.security.util.Util; +import gnu.javax.crypto.sasl.ClientMechanism; + +import java.io.IOException; +import java.security.InvalidKeyException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthenticationException; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * The CRAM-MD5 SASL client-side mechanism. + */ +public class CramMD5Client extends ClientMechanism implements SaslClient +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + public CramMD5Client() + { + super(Registry.SASL_CRAM_MD5_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + } + + protected void resetMechanism() throws SaslException + { + } + + // javax.security.sasl.SaslClient interface implementation ----------------- + + public boolean hasInitialResponse() + { + return false; + } + + public byte[] evaluateChallenge(final byte[] challenge) throws SaslException + { + if (challenge == null) + { + throw new SaslException("null challenge"); + } + try + { + final String username; + final char[] password; + Callback[] callbacks; + + if ((!properties.containsKey(Registry.SASL_USERNAME)) + && (!properties.containsKey(Registry.SASL_PASSWORD))) + { + callbacks = new Callback[2]; + + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + { + nameCB = new NameCallback("username: "); + } + else + { + nameCB = new NameCallback("username: ", defaultName); + } + final PasswordCallback pwdCB = new PasswordCallback("password: ", + false); + callbacks[0] = nameCB; + callbacks[1] = pwdCB; + this.handler.handle(callbacks); + username = nameCB.getName(); + password = pwdCB.getPassword(); + } + else + { + if (properties.containsKey(Registry.SASL_USERNAME)) + { + username = (String) properties.get(Registry.SASL_USERNAME); + } + else + { + callbacks = new Callback[1]; + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + { + nameCB = new NameCallback("username: "); + } + else + { + nameCB = new NameCallback("username: ", defaultName); + } + callbacks[0] = nameCB; + this.handler.handle(callbacks); + username = nameCB.getName(); + } + + if (properties.containsKey(Registry.SASL_PASSWORD)) + { + password = ((String) properties.get(Registry.SASL_PASSWORD)).toCharArray(); + } + else + { + callbacks = new Callback[1]; + final PasswordCallback pwdCB = new PasswordCallback( + "password: ", + false); + callbacks[0] = pwdCB; + this.handler.handle(callbacks); + password = pwdCB.getPassword(); + } + } + + if (password == null) + { + throw new SaslException("null password supplied"); + } + final byte[] digest; + try + { + digest = CramMD5Util.createHMac(password, challenge); + } + catch (InvalidKeyException x) + { + throw new AuthenticationException("evaluateChallenge()", x); + } + final String response = username + " " + + Util.toString(digest).toLowerCase(); + this.complete = true; + + return response.getBytes("UTF-8"); + } + catch (UnsupportedCallbackException x) + { + throw new AuthenticationException("evaluateChallenge()", x); + } + catch (IOException x) + { + throw new AuthenticationException("evaluateChallenge()", x); + } + } + + protected String getNegotiatedQOP() + { + return Registry.QOP_AUTH; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/crammd5/CramMD5Registry.java b/gnu/javax/crypto/sasl/crammd5/CramMD5Registry.java new file mode 100644 index 000000000..1c61cace4 --- /dev/null +++ b/gnu/javax/crypto/sasl/crammd5/CramMD5Registry.java @@ -0,0 +1,66 @@ +/* CramMD5Registry.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.crammd5; + +/** + * A list of properties common to CRAM-MD5 classes. + */ +public interface CramMD5Registry +{ + /** Name of the password file (used by the server) property. */ + String PASSWORD_FILE = "gnu.crypto.sasl.crammd5.password.file"; + + /** Default password file (used by the server) pathname. */ + String DEFAULT_PASSWORD_FILE = "/etc/passwd"; + + /** Name of the UID field in the plain password file. */ + String UID_FIELD = "crammd5.uid"; + + /** Name of the GID field in the plain password file. */ + String GID_FIELD = "crammd5.gid"; + + /** Name of the GECOS field in the plain password file. */ + String GECOS_FIELD = "crammd5.gecos"; + + /** Name of the DIR field in the plain password file. */ + String DIR_FIELD = "crammd5.dir"; + + /** Name of the SHELL field in the plain password file. */ + String SHELL_FIELD = "crammd5.shell"; +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/crammd5/CramMD5Server.java b/gnu/javax/crypto/sasl/crammd5/CramMD5Server.java new file mode 100644 index 000000000..d6622b6db --- /dev/null +++ b/gnu/javax/crypto/sasl/crammd5/CramMD5Server.java @@ -0,0 +1,185 @@ +/* CramMD5Server.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.crammd5; + +import gnu.java.security.Registry; +import gnu.java.security.util.Util; +import gnu.javax.crypto.sasl.NoSuchUserException; +import gnu.javax.crypto.sasl.ServerMechanism; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import javax.security.sasl.AuthenticationException; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +/** + * <p>The CRAM-MD5 SASL server-side mechanism.</p> + */ +public class CramMD5Server extends ServerMechanism implements SaslServer +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private byte[] msgID; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public CramMD5Server() + { + super(Registry.SASL_CRAM_MD5_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + } + + protected void resetMechanism() throws SaslException + { + } + + // javax.security.sasl.SaslServer interface implementation ----------------- + + public byte[] evaluateResponse(final byte[] response) throws SaslException + { + if (state == 0) + { + msgID = CramMD5Util.createMsgID(); + state++; + return msgID; + } + + final String responseStr = new String(response); + final int index = responseStr.lastIndexOf(" "); + final String username = responseStr.substring(0, index); + final byte[] responseDigest; + try + { + responseDigest = responseStr.substring(index + 1).getBytes("UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("evaluateResponse()", x); + } + + // Look up the password + final char[] password = lookupPassword(username); + + // Compute the digest + byte[] digest; + try + { + digest = CramMD5Util.createHMac(password, msgID); + } + catch (InvalidKeyException x) + { + throw new AuthenticationException("evaluateResponse()", x); + } + try + { + // digest = (new String(Util.toString(digest).toLowerCase())).getBytes("UTF-8"); + digest = Util.toString(digest).toLowerCase().getBytes("UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("evaluateResponse()", x); + } + + // Compare the received and computed digests + if (!Arrays.equals(digest, responseDigest)) + { + throw new AuthenticationException("Digest mismatch"); + } + state++; + return null; + } + + public boolean isComplete() + { + return (state == 2); + } + + protected String getNegotiatedQOP() + { + return Registry.QOP_AUTH; + } + + // Other instance methods -------------------------------------------------- + + private char[] lookupPassword(final String userName) throws SaslException + { + try + { + if (!authenticator.contains(userName)) + { + throw new NoSuchUserException(userName); + } + final Map userID = new HashMap(); + userID.put(Registry.SASL_USERNAME, userName); + final Map credentials = authenticator.lookup(userID); + final String password = (String) credentials.get(Registry.SASL_PASSWORD); + if (password == null) + { + throw new AuthenticationException("lookupPassword()", + new InternalError()); + } + return password.toCharArray(); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("lookupPassword()", x); + } + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/crammd5/CramMD5Util.java b/gnu/javax/crypto/sasl/crammd5/CramMD5Util.java new file mode 100644 index 000000000..6e7539349 --- /dev/null +++ b/gnu/javax/crypto/sasl/crammd5/CramMD5Util.java @@ -0,0 +1,137 @@ +/* CramMD5Util.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.crammd5; + +import gnu.java.security.Registry; +import gnu.java.security.util.Util; +import gnu.javax.crypto.mac.HMacFactory; +import gnu.javax.crypto.mac.IMac; + +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.InvalidKeyException; +import java.util.HashMap; + +import javax.security.sasl.SaslException; + +/** + * A package-private CRAM-MD5-specific utility class. + */ +class CramMD5Util +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + private CramMD5Util() + { + super(); + } + + // Class methods + // ------------------------------------------------------------------------- + + static byte[] createMsgID() throws SaslException + { + // <process-ID.clock@hostname> + final String encoded; + try + { + encoded = Util.toBase64(Thread.currentThread().getName().getBytes( + "UTF-8")); + } + catch (UnsupportedEncodingException x) + { + throw new SaslException("createMsgID()", x); + } + String hostname = "localhost"; + try + { + hostname = InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException ignored) + { + } + + final byte[] result; + try + { + result = new StringBuffer().append("<").append( + encoded.substring( + 0, + encoded.length())).append( + ".").append( + String.valueOf(System.currentTimeMillis())).append( + "@").append( + hostname).append( + ">").toString().getBytes( + "UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new SaslException("createMsgID()", x); + } + return result; + } + + static byte[] createHMac(final char[] passwd, final byte[] data) + throws InvalidKeyException, SaslException + { + final IMac mac = HMacFactory.getInstance(Registry.HMAC_NAME_PREFIX + + Registry.MD5_HASH); + final HashMap map = new HashMap(); + final byte[] km; + try + { + km = new String(passwd).getBytes("UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new SaslException("createHMac()", x); + } + map.put(IMac.MAC_KEY_MATERIAL, km); + mac.init(map); + mac.update(data, 0, data.length); + return mac.digest(); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/crammd5/PasswordFile.java b/gnu/javax/crypto/sasl/crammd5/PasswordFile.java new file mode 100644 index 000000000..081af4615 --- /dev/null +++ b/gnu/javax/crypto/sasl/crammd5/PasswordFile.java @@ -0,0 +1,301 @@ +/* PasswordFile.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.crammd5; + +import gnu.javax.crypto.sasl.NoSuchUserException; +import gnu.javax.crypto.sasl.UserAlreadyExistsException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +/** + * The CRAM-MD5 password file representation. + */ +public class PasswordFile +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private static String DEFAULT_FILE; + static + { + DEFAULT_FILE = System.getProperty(CramMD5Registry.PASSWORD_FILE, + CramMD5Registry.DEFAULT_PASSWORD_FILE); + } + + private HashMap entries; + + private File passwdFile; + + private long lastmod; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public PasswordFile() throws IOException + { + this(DEFAULT_FILE); + } + + public PasswordFile(final File pwFile) throws IOException + { + this(pwFile.getAbsolutePath()); + } + + public PasswordFile(final String fileName) throws IOException + { + passwdFile = new File(fileName); + update(); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + public synchronized void add(final String user, final String passwd, + final String[] attributes) throws IOException + { + checkCurrent(); // check if the entry exists + if (entries.containsKey(user)) + { + throw new UserAlreadyExistsException(user); + } + if (attributes.length != 5) + { + throw new IllegalArgumentException("Wrong number of attributes"); + } + + final String[] fields = new String[7]; // create the new entry + fields[0] = user; + fields[1] = passwd; + System.arraycopy(attributes, 0, fields, 2, 5); + entries.put(user, fields); + savePasswd(); + } + + public synchronized void changePasswd(final String user, final String passwd) + throws IOException + { + checkCurrent(); + if (!entries.containsKey(user)) + { // check if the entry exists + throw new NoSuchUserException(user); + } + + final String[] fields = (String[]) entries.get(user); // get the existing entry + fields[1] = passwd; // modify the password field + entries.remove(user); // delete the existing entry + entries.put(user, fields); // add the new entry + + savePasswd(); + } + + public synchronized String[] lookup(final String user) throws IOException + { + checkCurrent(); + if (!entries.containsKey(user)) + { + throw new NoSuchUserException(user); + } + return (String[]) entries.get(user); + } + + public synchronized boolean contains(final String s) throws IOException + { + checkCurrent(); + + return entries.containsKey(s); + } + + private synchronized void update() throws IOException + { + lastmod = passwdFile.lastModified(); + readPasswd(new FileInputStream(passwdFile)); + } + + private void checkCurrent() throws IOException + { + if (passwdFile.lastModified() > lastmod) + { + update(); + } + } + + private synchronized void readPasswd(final InputStream in) throws IOException + { + final BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line; + entries = new HashMap(); + while ((line = din.readLine()) != null) + { + final String[] fields = new String[7]; + final StringTokenizer st = new StringTokenizer(line, ":", true); + try + { + fields[0] = st.nextToken(); // username + st.nextToken(); + + fields[1] = st.nextToken(); // passwd + if (fields[1].equals(":")) + { + fields[1] = ""; + } + else + { + st.nextToken(); + } + + fields[2] = st.nextToken(); // uid + if (fields[2].equals(":")) + { + fields[2] = ""; + } + else + { + st.nextToken(); + } + + fields[3] = st.nextToken(); // gid + if (fields[3].equals(":")) + { + fields[3] = ""; + } + else + { + st.nextToken(); + } + + fields[4] = st.nextToken(); // gecos + if (fields[4].equals(":")) + { + fields[4] = ""; + } + else + { + st.nextToken(); + } + + fields[5] = st.nextToken(); // dir + if (fields[5].equals(":")) + { + fields[5] = ""; + } + else + { + st.nextToken(); + } + + fields[6] = st.nextToken(); // shell + if (fields[6].equals(":")) + { + fields[6] = ""; + } + } + catch (NoSuchElementException x) + { + continue; + } + + entries.put(fields[0], fields); + } + } + + private synchronized void savePasswd() throws IOException + { + if (passwdFile != null) + { + final FileOutputStream fos = new FileOutputStream(passwdFile); + PrintWriter pw = null; + try + { + pw = new PrintWriter(fos); + String key; + String[] fields; + StringBuffer sb; + int i; + for (Iterator it = entries.keySet().iterator(); it.hasNext();) + { + key = (String) it.next(); + fields = (String[]) entries.get(key); + sb = new StringBuffer(fields[0]); + for (i = 1; i < fields.length; i++) + { + sb.append(":").append(fields[i]); + } + pw.println(sb.toString()); + } + } + finally + { + if (pw != null) + { + try + { + pw.flush(); + } + finally + { + pw.close(); + } + } + try + { + fos.close(); + } + catch (IOException ignored) + { + } + lastmod = passwdFile.lastModified(); + } + } + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/plain/PasswordFile.java b/gnu/javax/crypto/sasl/plain/PasswordFile.java new file mode 100644 index 000000000..4ef6b8541 --- /dev/null +++ b/gnu/javax/crypto/sasl/plain/PasswordFile.java @@ -0,0 +1,309 @@ +/* PasswordFile.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +import gnu.classpath.SystemProperties; + +import gnu.javax.crypto.sasl.NoSuchUserException; +import gnu.javax.crypto.sasl.UserAlreadyExistsException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.NoSuchElementException; + +/** + * A representation of a Plain password file. + */ +public class PasswordFile +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private static String DEFAULT_FILE; + static + { + DEFAULT_FILE = SystemProperties.getProperty(PlainRegistry.PASSWORD_FILE, + PlainRegistry.DEFAULT_PASSWORD_FILE); + } + + private Hashtable entries; + + private File passwdFile; + + // private String[] last_params; + private long lastmod; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public PasswordFile() throws IOException + { + this(DEFAULT_FILE); + } + + public PasswordFile(File pwFile) throws IOException + { + this(pwFile.getAbsolutePath()); + } + + public PasswordFile(String fileName) throws IOException + { + passwdFile = new File(fileName); + update(); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + public synchronized void add(String user, String passwd, String[] attributes) + throws IOException + { + checkCurrent(); + if (entries.containsKey(user)) + { + throw new UserAlreadyExistsException(user); + } + if (attributes.length != 5) + { + throw new IllegalArgumentException("Wrong number of attributes"); + } + // create the new entry + String[] fields = new String[7]; + fields[0] = user; + fields[1] = passwd; + System.arraycopy(attributes, 0, fields, 2, 5); + entries.put(user, fields); + + savePasswd(); + } + + public synchronized void changePasswd(String user, String passwd) + throws IOException + { + checkCurrent(); + if (!entries.containsKey(user)) + { + throw new NoSuchUserException(user); + } + + String[] fields = (String[]) entries.get(user); // get the existing entry + fields[1] = passwd; // modify the password field + entries.remove(user); // delete the existing entry + entries.put(user, fields); // add the new entry + + savePasswd(); + } + + public synchronized String[] lookup(String user) throws IOException + { + checkCurrent(); + if (!entries.containsKey(user)) + { + throw new NoSuchUserException(user); + } + return (String[]) entries.get(user); + } + + public synchronized boolean contains(String s) throws IOException + { + checkCurrent(); + return entries.containsKey(s); + } + + //----------------------------------------------------------------// + + private synchronized void update() throws IOException + { + lastmod = passwdFile.lastModified(); + readPasswd(new FileInputStream(passwdFile)); + } + + private void checkCurrent() throws IOException + { + if (passwdFile.lastModified() > lastmod) + { + update(); + } + } + + private synchronized void readPasswd(InputStream in) throws IOException + { + BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line; + entries = new Hashtable(); + String[] fields = new String[7]; + while ((line = din.readLine()) != null) + { + StringTokenizer st = new StringTokenizer(line, ":", true); + try + { + fields[0] = st.nextToken(); // username + st.nextToken(); + + fields[1] = st.nextToken(); // passwd + if (fields[1].equals(":")) + { + fields[1] = ""; + } + else + { + st.nextToken(); + } + + fields[2] = st.nextToken(); // uid + if (fields[2].equals(":")) + { + fields[2] = ""; + } + else + { + st.nextToken(); + } + + fields[3] = st.nextToken(); // gid + if (fields[3].equals(":")) + { + fields[3] = ""; + } + else + { + st.nextToken(); + } + + fields[4] = st.nextToken(); // gecos + if (fields[4].equals(":")) + { + fields[4] = ""; + } + else + { + st.nextToken(); + } + + fields[5] = st.nextToken(); // dir + if (fields[5].equals(":")) + { + fields[5] = ""; + } + else + { + st.nextToken(); + } + + fields[6] = st.nextToken(); // shell + if (fields[6].equals(":")) + { + fields[6] = ""; + } + } + catch (NoSuchElementException ignored) + { + continue; + } + + entries.put(fields[0], fields); + } + } + + private synchronized void savePasswd() throws IOException + { + if (passwdFile != null) + { + FileOutputStream fos = new FileOutputStream(passwdFile); + PrintWriter pw = null; + try + { + pw = new PrintWriter(fos); + String key; + String[] fields; + StringBuffer sb; + Enumeration keys = entries.keys(); + while (keys.hasMoreElements()) + { + key = (String) keys.nextElement(); + fields = (String[]) entries.get(key); + sb = new StringBuffer(fields[0]); + for (int i = 1; i < fields.length; i++) + { + sb.append(":" + fields[i]); + } + pw.println(sb.toString()); + } + } + finally + { + if (pw != null) + { + try + { + pw.flush(); + } + finally + { + pw.close(); + } + } + if (fos != null) + { + try + { + fos.close(); + } + catch (IOException ignored) + { + } + } + lastmod = passwdFile.lastModified(); + } + } + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/plain/PlainAuthInfoProvider.java b/gnu/javax/crypto/sasl/plain/PlainAuthInfoProvider.java new file mode 100644 index 000000000..9882ce9bb --- /dev/null +++ b/gnu/javax/crypto/sasl/plain/PlainAuthInfoProvider.java @@ -0,0 +1,206 @@ +/* PlainAuthInfoProvider.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.IAuthInfoProvider; +import gnu.javax.crypto.sasl.NoSuchUserException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.security.sasl.AuthenticationException; + +/** + * The PLAIN mechanism authentication information provider implementation. + */ +public class PlainAuthInfoProvider implements IAuthInfoProvider, PlainRegistry +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private PasswordFile passwordFile = null; + + // Constructor(s) + // ------------------------------------------------------------------------- + + // implicit 0-args constrcutor + + // Class methods + // ------------------------------------------------------------------------- + + // IAuthInfoProvider interface implementation + // ------------------------------------------------------------------------- + + public void activate(Map context) throws AuthenticationException + { + try + { + if (context == null) + { + passwordFile = new PasswordFile(); + } + else + { + String pfn = (String) context.get(PASSWORD_FILE); + if (pfn == null) + { + passwordFile = new PasswordFile(); + } + else + { + passwordFile = new PasswordFile(pfn); + } + } + } + catch (IOException x) + { + throw new AuthenticationException("activate()", x); + } + } + + public void passivate() throws AuthenticationException + { + passwordFile = null; + } + + public boolean contains(String userName) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("contains()", + new IllegalStateException()); + } + boolean result = false; + try + { + result = passwordFile.contains(userName); + } + catch (IOException x) + { + throw new AuthenticationException("contains()", x); + } + return result; + } + + public Map lookup(Map userID) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("lookup()", + new IllegalStateException()); + } + Map result = new HashMap(); + try + { + String userName = (String) userID.get(Registry.SASL_USERNAME); + if (userName == null) + { + throw new NoSuchUserException(""); + } + String[] data = passwordFile.lookup(userName); + result.put(Registry.SASL_USERNAME, data[0]); + result.put(Registry.SASL_PASSWORD, data[1]); + result.put(UID_FIELD, data[2]); + result.put(GID_FIELD, data[3]); + result.put(GECOS_FIELD, data[4]); + result.put(DIR_FIELD, data[5]); + result.put(SHELL_FIELD, data[6]); + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + else + { + throw new AuthenticationException("lookup()", x); + } + } + return result; + } + + public void update(Map userCredentials) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("update()", + new IllegalStateException()); + } + try + { + String userName = (String) userCredentials.get(Registry.SASL_USERNAME); + String password = (String) userCredentials.get(Registry.SASL_PASSWORD); + String uid = (String) userCredentials.get(UID_FIELD); + String gid = (String) userCredentials.get(GID_FIELD); + String gecos = (String) userCredentials.get(GECOS_FIELD); + String dir = (String) userCredentials.get(DIR_FIELD); + String shell = (String) userCredentials.get(SHELL_FIELD); + if (uid == null || gid == null || gecos == null || dir == null + || shell == null) + { + passwordFile.changePasswd(userName, password); + } + else + { + String[] attributes = new String[] { uid, gid, gecos, dir, shell }; + passwordFile.add(userName, password, attributes); + } + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + else + { + throw new AuthenticationException("update()", x); + } + } + } + + public Map getConfiguration(String mode) throws AuthenticationException + { + throw new AuthenticationException("", new UnsupportedOperationException()); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/plain/PlainClient.java b/gnu/javax/crypto/sasl/plain/PlainClient.java new file mode 100644 index 000000000..066db3770 --- /dev/null +++ b/gnu/javax/crypto/sasl/plain/PlainClient.java @@ -0,0 +1,193 @@ +/* PlainClient.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.ClientMechanism; + +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; + +/** + * <p>The PLAIN SASL client-side mechanism.</p> + */ +public class PlainClient extends ClientMechanism implements SaslClient +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + public PlainClient() + { + super(Registry.SASL_PLAIN_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + } + + protected void resetMechanism() throws SaslException + { + } + + // javax.security.sasl.SaslClient interface implementation ----------------- + + public boolean hasInitialResponse() + { + return true; + } + + public byte[] evaluateChallenge(final byte[] challenge) throws SaslException + { + try + { + final String username; + final char[] password; + Callback[] callbacks; + + if ((!properties.containsKey(Registry.SASL_USERNAME)) + && (!properties.containsKey(Registry.SASL_PASSWORD))) + { + callbacks = new Callback[2]; + + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + { + nameCB = new NameCallback("username: "); + } + else + { + nameCB = new NameCallback("username: ", defaultName); + } + final PasswordCallback pwdCB = new PasswordCallback("password: ", + false); + callbacks[0] = nameCB; + callbacks[1] = pwdCB; + this.handler.handle(callbacks); + username = nameCB.getName(); + password = pwdCB.getPassword(); + } + else + { + if (properties.containsKey(Registry.SASL_USERNAME)) + { + username = (String) properties.get(Registry.SASL_USERNAME); + } + else + { + callbacks = new Callback[1]; + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + { + nameCB = new NameCallback("username: "); + } + else + { + nameCB = new NameCallback("username: ", defaultName); + } + callbacks[0] = nameCB; + this.handler.handle(callbacks); + username = nameCB.getName(); + } + + if (properties.containsKey(Registry.SASL_PASSWORD)) + { + password = ((String) properties.get(Registry.SASL_PASSWORD)).toCharArray(); + } + else + { + callbacks = new Callback[1]; + final PasswordCallback pwdCB = new PasswordCallback( + "password: ", + false); + callbacks[0] = pwdCB; + this.handler.handle(callbacks); + password = pwdCB.getPassword(); + } + } + + if (password == null) + { + throw new SaslException("null password supplied"); + } + final StringBuffer sb = new StringBuffer(); + if (authorizationID != null) + { + sb.append(authorizationID); + } + sb.append('\0'); + sb.append(username); + sb.append('\0'); + sb.append(password); + this.complete = true; + + final byte[] response = sb.toString().getBytes("UTF-8"); + return response; + } + catch (Exception x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("evaluateChallenge()", x); + } + } + + protected String getNegotiatedQOP() + { + return Registry.QOP_AUTH; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/plain/PlainRegistry.java b/gnu/javax/crypto/sasl/plain/PlainRegistry.java new file mode 100644 index 000000000..0b48c0ad3 --- /dev/null +++ b/gnu/javax/crypto/sasl/plain/PlainRegistry.java @@ -0,0 +1,67 @@ +/* PlainRegistry.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +public interface PlainRegistry +{ + + // Constants + // ------------------------------------------------------------------------- + + /** Name of PLAIN password file property. */ + String PASSWORD_FILE = "gnu.crypto.sasl.plain.password.file"; + + /** Default fully qualified pathname of the PLAIN password file. */ + String DEFAULT_PASSWORD_FILE = "/etc/tpasswd"; + + /** Name of the UID field in the plain password file. */ + String UID_FIELD = "plain.uid"; + + /** Name of the GID field in the plain password file. */ + String GID_FIELD = "plain.gid"; + + /** Name of the GECOS field in the plain password file. */ + String GECOS_FIELD = "plain.gecos"; + + /** Name of the DIR field in the plain password file. */ + String DIR_FIELD = "plain.dir"; + + /** Name of the SHELL field in the plain password file. */ + String SHELL_FIELD = "plain.shell"; +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/plain/PlainServer.java b/gnu/javax/crypto/sasl/plain/PlainServer.java new file mode 100644 index 000000000..205688473 --- /dev/null +++ b/gnu/javax/crypto/sasl/plain/PlainServer.java @@ -0,0 +1,196 @@ +/* PlainServer.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.NoSuchUserException; +import gnu.javax.crypto.sasl.ServerMechanism; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +/** + * <p>The PLAIN SASL server-side mechanism.</p> + */ +public class PlainServer extends ServerMechanism implements SaslServer +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // Constructor(s) + // ------------------------------------------------------------------------- + + public PlainServer() + { + super(Registry.SASL_PLAIN_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + } + + protected void resetMechanism() throws SaslException + { + } + + // javax.security.sasl.SaslServer interface implementation ----------------- + + public byte[] evaluateResponse(final byte[] response) throws SaslException + { + if (response == null) + { + return null; + } + try + { + final String nullStr = new String("\0"); + final StringTokenizer strtok = new StringTokenizer( + new String(response), + nullStr, true); + + authorizationID = strtok.nextToken(); + if (!authorizationID.equals(nullStr)) + { + strtok.nextToken(); + } + else + { + authorizationID = null; + } + final String id = strtok.nextToken(); + if (id.equals(nullStr)) + { + throw new SaslException("No identity given"); + } + if (authorizationID == null) + { + authorizationID = id; + } + if ((!authorizationID.equals(nullStr)) && (!authorizationID.equals(id))) + { + throw new SaslException("Delegation not supported"); + } + strtok.nextToken(); + final byte[] pwd; + try + { + pwd = strtok.nextToken().getBytes("UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new SaslException("evaluateResponse()", x); + } + if (pwd == null) + { + throw new SaslException("No password given"); + } + final byte[] password; + try + { + password = new String(lookupPassword(id)).getBytes("UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new SaslException("evaluateResponse()", x); + } + if (!Arrays.equals(pwd, password)) + { + throw new SaslException("Password incorrect"); + } + this.complete = true; + return null; + } + catch (NoSuchElementException x) + { + throw new SaslException("evaluateResponse()", x); + } + } + + protected String getNegotiatedQOP() + { + return Registry.QOP_AUTH; + } + + // other methods ----------------------------------------------------------- + + private char[] lookupPassword(final String userName) throws SaslException + { + try + { + if (!authenticator.contains(userName)) + { + throw new NoSuchUserException(userName); + } + final Map userID = new HashMap(); + userID.put(Registry.SASL_USERNAME, userName); + final Map credentials = authenticator.lookup(userID); + final String password = (String) credentials.get(Registry.SASL_PASSWORD); + if (password == null) + { + throw new SaslException("lookupPassword()", new InternalError()); + } + return password.toCharArray(); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("lookupPassword()", x); + } + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/CALG.java b/gnu/javax/crypto/sasl/srp/CALG.java new file mode 100644 index 000000000..6215783d6 --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/CALG.java @@ -0,0 +1,292 @@ +/* CALG.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.javax.crypto.assembly.Assembly; +import gnu.javax.crypto.assembly.Cascade; +import gnu.javax.crypto.assembly.Direction; +import gnu.javax.crypto.assembly.Stage; +import gnu.javax.crypto.assembly.Transformer; +import gnu.javax.crypto.assembly.TransformerException; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.mode.IMode; +import gnu.javax.crypto.mode.ModeFactory; +import gnu.javax.crypto.pad.IPad; +import gnu.javax.crypto.pad.PadFactory; +import gnu.javax.crypto.sasl.ConfidentialityException; + +import java.util.HashMap; + +import javax.security.sasl.SaslException; + +/** + * <p>A Factory class that returns CALG (Confidentiality Algorithm) instances + * that operate as described in the draft-burdis-cat-sasl-srp-08.</p> + * + * <p>The designated CALG block cipher should be used in OFB (Output Feedback + * Block) mode in the ISO variant, as described in <i>The Handbook of Applied + * Cryptography</i>, algorithm 7.20.</p> + * + * <p>Let <code>k</code> be the block size of the chosen symmetric key block + * cipher algorithm; e.g. for AES this is <code>128</code> bits or <code>16</code> + * octets. The OFB mode used shall be of length/size <code>k</code>.</p> + * + * <p>It is recommended that block ciphers operating in OFB mode be used with an + * Initial Vector (the mode's IV). In such a mode of operation - OFB with key + * re-use - the IV need not be secret. For the mechanism in question the IVs + * shall be a random octet sequence of <code>k</code> bytes.</p> + * + * The input data to the confidentiality protection algorithm shall be + * a multiple of the symmetric cipher block size <code>k</code>. When the input + * length is not a multiple of <code>k</code> octets, the data shall be padded + * according to the following scheme:</p> + * + * <p>Assuming the length of the input is <code>l</code> octets, + * <code>(k - (l mod k))</code> octets, all having the value + * <code>(k - (l mod k))</code>, shall be appended to the original data. In + * other words, the input is padded at the trailing end with one of the + * following sequences:</p> + * + * <pre> + * + * 01 -- if l mod k = k-1 + * 02 02 -- if l mod k = k-2 + * ... + * ... + * ... + * k k ... k k -- if l mod k = 0 + *</pre> + * + * <p>The padding can be removed unambiguously since all input is padded and no + * padding sequence is a suffix of another. This padding method is well-defined + * if and only if <code>k < 256</code> octets, which is the case with + * symmetric key block ciphers today, and in the forseeable future.</p> + */ +public final class CALG +{ + + // Constants and variables + // -------------------------------------------------------------------------- + + private Assembly assembly; + + private Object modeNdx; // initialisation key of the cascade's attributes + + private int blockSize; // the underlying cipher's blocksize == IV length + + private int keySize; // the underlying cipher's key size (in bytes). + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** Private constructor to enforce instantiation through Factory method. */ + private CALG(final int blockSize, final int keySize, final Object modeNdx, + final Assembly assembly) + { + super(); + + this.blockSize = blockSize; + this.keySize = keySize; + this.modeNdx = modeNdx; + this.assembly = assembly; + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns an instance of a SASL-SRP CALG implementation.</p> + * + * @param algorithm the name of the symmetric cipher algorithm. + * @return an instance of this object. + */ + static synchronized CALG getInstance(final String algorithm) + { + final IBlockCipher cipher = CipherFactory.getInstance(algorithm); + final int blockSize = cipher.defaultBlockSize(); + final int keySize = cipher.defaultKeySize(); + final Cascade ofbCipher = new Cascade(); + final Object modeNdx = ofbCipher.append(Stage.getInstance( + ModeFactory.getInstance( + Registry.OFB_MODE, + cipher, + blockSize), + Direction.FORWARD)); + final IPad pkcs7 = PadFactory.getInstance(Registry.PKCS7_PAD); + // the passed IV may be longer that what we need. ensure correct length + // byte[] realIV = null; + // if (iv.length == blockSize) { + // realIV = iv; + // } else { + // realIV = new byte[blockSize]; + // if (iv.length > blockSize) { + // System.arraycopy(iv, 0, realIV, 0, blockSize); + // } else { // shouldnt happen + // System.arraycopy(iv, 0, realIV, 0, iv.length); + // } + // } + + // HashMap modeAttributes = new HashMap(); + // modeAttributes.put(IBlockCipher.KEY_MATERIAL, K.clone()); + // modeAttributes.put(IMode.IV, realIV); + + final Assembly asm = new Assembly(); + asm.addPreTransformer(Transformer.getCascadeTransformer(ofbCipher)); + asm.addPreTransformer(Transformer.getPaddingTransformer(pkcs7)); + + // HashMap attributes = new HashMap(); + // attributes.put(Assembly.DIRECTION, dir); + // attributes.put(modeNdx, modeAttributes); + // try { + // asm.init(attributes); + // } catch (TransformerException x) { + // throw new SaslException("getInstance()", x); + // } + + return new CALG(blockSize, keySize, modeNdx, asm); + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Initialises a SASL-SRP CALG implementation.</p> + * + * @param kdf the key derivation function. + * @param iv the initial vector value to use. + * @param dir whether this CALG is used for encryption or decryption. + */ + // public void init(byte[] K, byte[] iv, Direction dir) throws SaslException { + public void init(final KDF kdf, final byte[] iv, final Direction dir) + throws SaslException + { + // IBlockCipher cipher = CipherFactory.getInstance(algorithm); + // int blockSize = cipher.defaultBlockSize(); + // Cascade ofbCipher = new Cascade(); + // Object modeNdx = ofbCipher.append( + // Stage.getInstace( + // ModeFactory.getInstance(Registry.OFB_MODE, cipher, blockSize), + // Direction.FORWARD)); + // IPad pkcs7 = PadFactory.getInstance(Registry.PKCS7_PAD); + // the passed IV may be longer that what we need. ensure correct length + final byte[] realIV; + if (iv.length == blockSize) + { + realIV = iv; + } + else + { + realIV = new byte[blockSize]; + if (iv.length > blockSize) + { + System.arraycopy(iv, 0, realIV, 0, blockSize); + } + else + { // shouldnt happen + System.arraycopy(iv, 0, realIV, 0, iv.length); + } + } + + final HashMap modeAttributes = new HashMap(); + // modeAttributes.put(IBlockCipher.KEY_MATERIAL, K.clone()); + final byte[] sk = kdf.derive(keySize); + modeAttributes.put(IBlockCipher.KEY_MATERIAL, sk); + //System.out.println("**** Initialised CALG with: "+gnu.crypto.util.Util.dumpString(sk)); + modeAttributes.put(IMode.IV, realIV); + + // Assembly asm = new Assembly(); + // asm.addPreTransformer(Transformer.getCascadeTransformer(ofbCipher)); + // asm.addPreTransformer(Transformer.getPaddingTransformer(pkcs7)); + + final HashMap attributes = new HashMap(); + attributes.put(Assembly.DIRECTION, dir); + attributes.put(modeNdx, modeAttributes); + try + { + // asm.init(attributes); + assembly.init(attributes); + } + catch (TransformerException x) + { + throw new SaslException("getInstance()", x); + } + + // return new CALG(asm); + } + + /** + * <p>Encrypts or decrypts, depending on the mode already set, a designated + * array of bytes and returns the result.</p> + * + * @param data the data to encrypt/decrypt. + * @return the decrypted/encrypted result. + * @throws ConfidentialityException if an exception occurs duirng the process. + */ + public byte[] doFinal(final byte[] data) throws ConfidentialityException + { + return doFinal(data, 0, data.length); + } + + /** + * <p>Encrypts or decrypts, depending on the mode already set, a designated + * array of bytes and returns the result.</p> + * + * @param data the data to encrypt/decrypt. + * @param offset where to start in <code>data</code>. + * @param length how many bytes to consider in <code>data</code>. + * @return the decrypted/encrypted result. + * @throws ConfidentialityException if an exception occurs duirng the process. + */ + public byte[] doFinal(final byte[] data, final int offset, final int length) + throws ConfidentialityException + { + final byte[] result; + try + { + result = assembly.lastUpdate(data, offset, length); + } + catch (TransformerException x) + { + throw new ConfidentialityException("doFinal()", x); + } + return result; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/ClientStore.java b/gnu/javax/crypto/sasl/srp/ClientStore.java new file mode 100644 index 000000000..ce16f4aa7 --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/ClientStore.java @@ -0,0 +1,173 @@ +/* ClientStore.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import java.util.HashMap; + +/** + * <p>The client-side implementation of the SRP security context store.</p> + */ +public class ClientStore +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + /** The underlying singleton. */ + private static ClientStore singleton = null; + + /** The map of uid --> SASL Security Context record. */ + private static final HashMap uid2ssc = new HashMap(); + + /** The map of sid --> Session timing record. */ + private static final HashMap uid2ttl = new HashMap(); + + /** A synchronisation lock. */ + private static final Object lock = new Object(); + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** Private constructor to enforce Singleton pattern. */ + private ClientStore() + { + super(); + + // TODO: add a cleaning timer thread + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns the classloader Singleton.</p> + * + * @return the classloader Singleton instance. + */ + static synchronized final ClientStore instance() + { + if (singleton == null) + { + singleton = new ClientStore(); + } + return singleton; + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns a boolean flag indicating if the designated client's session is + * still alive or not.</p> + * + * @param uid the identifier of the client whose session to check. + * @return <code>true</code> if the designated client's session is still + * alive. <code>false</code> otherwise. + */ + boolean isAlive(final String uid) + { + final boolean result; + synchronized (lock) + { + final Object obj = uid2ssc.get(uid); + result = (obj != null); + if (result) + { // is it still alive? + final StoreEntry sto = (StoreEntry) uid2ttl.get(uid); + if (!sto.isAlive()) + { // invalidate it + uid2ssc.remove(uid); + uid2ttl.remove(uid); + } + } + } + return result; + } + + /** + * <p>Records a mapping between a client's unique identifier and its security + * context.</p> + * + * @param uid the unique identifier of the SRP client for which the session + * is to be cached. + * @param ttl the session's Time-To-Live indicator (in seconds). + * @param ctx the client's security context. + */ + void cacheSession(final String uid, final int ttl, final SecurityContext ctx) + { + synchronized (lock) + { + uid2ssc.put(uid, ctx); + uid2ttl.put(uid, new StoreEntry(ttl)); + } + } + + /** + * <p>Removes the mapping between the designated SRP client unique identifier + * and the its session security context (and other timing information).</p> + * + * @param uid the identifier of the client whose session is to invalidate. + */ + void invalidateSession(final String uid) + { + synchronized (lock) + { + uid2ssc.remove(uid); + uid2ttl.remove(uid); + } + } + + /** + * <p>Returns an SRP client's security context record mapped by that client's + * unique identifier.</p> + * + * @param uid the identifier of the client whose session is to restore. + * @return the SRP client's security context. + */ + SecurityContext restoreSession(final String uid) + { + final SecurityContext result; + synchronized (lock) + { + result = (SecurityContext) uid2ssc.remove(uid); + uid2ttl.remove(uid); + } + return result; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/IALG.java b/gnu/javax/crypto/sasl/srp/IALG.java new file mode 100644 index 000000000..cfaf22b1e --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/IALG.java @@ -0,0 +1,159 @@ +/* IALG.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.javax.crypto.mac.IMac; +import gnu.javax.crypto.mac.MacFactory; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; + +import javax.security.sasl.SaslException; + +/** + * <p>A Factory class that returns IALG (Integrity Algorithm) instances that + * operate as described in the draft-burdis-cat-sasl-srp-04 and later.</p> + * + * @version $Revision: 1.1 $ + */ +public final class IALG implements Cloneable +{ + + // Constants and variables + // -------------------------------------------------------------------------- + + private IMac hmac; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** Private constructor to enforce instantiation through Factory method. */ + private IALG(final IMac hmac) + { + super(); + + this.hmac = hmac; + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns an instance of a SASL-SRP IALG implementation.</p> + * + * @param algorithm the name of the HMAC algorithm. + * @return an instance of this object. + */ + static synchronized IALG getInstance(final String algorithm) + throws SaslException + { + final IMac hmac; + hmac = MacFactory.getInstance(algorithm); + if (hmac == null) + { + throw new SaslException("getInstance()", + new NoSuchAlgorithmException(algorithm)); + } + // try { + // byte[] sk = (byte[]) K.clone(); + // HashMap map = new HashMap(); + // map.put(IMac.MAC_KEY_MATERIAL, sk); + // hmac.init(map); + // } catch (InvalidKeyException x) { + // throw new SaslException("getInstance()", x); + // } + return new IALG(hmac); + } + + // Instance methods + // ------------------------------------------------------------------------- + + // Cloneable interface implementation -------------------------------------- + + public Object clone() throws CloneNotSupportedException + { + return new IALG((IMac) hmac.clone()); + } + + // other methdds ----------------------------------------------------------- + + // public void init(final byte[] K) throws SaslException { + public void init(final KDF kdf) throws SaslException + { + try + { + // final byte[] sk = (byte[]) K.clone(); + final byte[] sk = kdf.derive(hmac.macSize()); + final HashMap map = new HashMap(); + map.put(IMac.MAC_KEY_MATERIAL, sk); + hmac.init(map); + //System.out.println("**** Initialised IALG with: "+gnu.crypto.util.Util.dumpString(sk)); + } + catch (InvalidKeyException x) + { + throw new SaslException("getInstance()", x); + } + } + + public void update(final byte[] data) + { + hmac.update(data, 0, data.length); + } + + public void update(final byte[] data, final int offset, final int length) + { + hmac.update(data, offset, length); + } + + public byte[] doFinal() + { + return hmac.digest(); + } + + /** + * <p>Returns the length (in bytes) of this SASL SRP Integrity Algorithm.</p> + * + * @return the length, in bytes, of this integrity protection algorithm. + */ + public int length() + { + return hmac.macSize(); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/KDF.java b/gnu/javax/crypto/sasl/srp/KDF.java new file mode 100644 index 000000000..161ec78b6 --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/KDF.java @@ -0,0 +1,157 @@ +/* KDF.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.java.security.prng.LimitReachedException; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.prng.UMacGenerator; + +import java.util.HashMap; +import java.util.Random; + +/** + * <p>The SASL-SRP KDF implementation, which is also used, depending on how it + * was instantiated, as a secure Pseudo Random Number Generator.</p> + */ +public class KDF +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private static final int AES_BLOCK_SIZE = 16; // default block size for the AES + + private static final int AES_KEY_SIZE = 16; // default key size for the AES + + /** The shared secret K to use. */ + // private byte[] keyMaterial; + /** The underlying UMAC Generator instance. */ + private UMacGenerator umac = null; + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** + * <p>Constructs an instance of the <code>KDF</code> initialised with the + * designated shared secret bytes.</p> + * + * @param keyMaterial the SASL SRP shared secret (K) bytes. + */ + private KDF(final byte[] keyMaterial, final int ndx) + { + super(); + + // if (ndx != 0) { + // this.keyMaterial = (byte[]) keyMaterial.clone(); + // } + final HashMap map = new HashMap(); + map.put(UMacGenerator.CIPHER, Registry.AES_CIPHER); + map.put(UMacGenerator.INDEX, new Integer(ndx)); + map.put(IBlockCipher.CIPHER_BLOCK_SIZE, new Integer(AES_BLOCK_SIZE)); + final byte[] key = new byte[AES_KEY_SIZE]; + System.arraycopy(keyMaterial, 0, key, 0, AES_KEY_SIZE); + map.put(IBlockCipher.KEY_MATERIAL, key); + + umac = new UMacGenerator(); + umac.init(map); + //System.out.println("**** Initialised KDF with: "+gnu.crypto.util.Util.dumpString(key)); + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>A Factory mehod that returns an instance of a <code>KDF</code> based on + * supplied seed data.</p> + * + * @param K the SASL SRP shared secret for a <code>KDF</code> to be used for + * <i>CALG</i> and <i>IALG</i> setup. <code>null</code> otherwise. + * @return an instance of a <code>KDF</code>. + */ + static final KDF getInstance(final byte[] K) + { + int ndx = -1; + final byte[] keyMaterial; + if (K != null) + { + keyMaterial = K; + ndx = 0; + } + else + { + keyMaterial = new byte[AES_BLOCK_SIZE]; + ndx = new Random ().nextInt (256); // XXX does this need to be secure? + } + return new KDF(keyMaterial, ndx); + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns a designated number of bytes suitable for use in the SASL SRP + * mechanism.</p> + * + * @param length the number of bytes needed. + * @return a byte array containing the generated/selected bytes. + */ + public synchronized byte[] derive(final int length) + { + final byte[] result = new byte[length]; + // if (keyMaterial == null || length > keyMaterial.length) { + try + { + umac.nextBytes(result, 0, length); + } + catch (IllegalStateException x) + { // should not happen + x.printStackTrace(System.err); + } + catch (LimitReachedException x) + { // idem + x.printStackTrace(System.err); + } + // } else { + // System.arraycopy(keyMaterial, 0, result, 0, length); + // } + + return result; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/PasswordFile.java b/gnu/javax/crypto/sasl/srp/PasswordFile.java new file mode 100644 index 000000000..1628a4167 --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/PasswordFile.java @@ -0,0 +1,699 @@ +/* PasswordFile.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.java.security.hash.IMessageDigest; +import gnu.java.security.util.Util; +import gnu.javax.crypto.key.srp6.SRPAlgorithm; +import gnu.javax.crypto.sasl.NoSuchUserException; +import gnu.javax.crypto.sasl.UserAlreadyExistsException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +/** + * <p>The implementation of SRP password files.</p> + * + * <p>For SRP, there are three (3) files: + * <ol> + * <li>The password configuration file: tpasswd.conf. It contains the pairs + * <N,g> indexed by a number for each pair used for a user. By default, + * this file's pathname is constructed from the base password file pathname + * by prepending it with the ".conf" suffix.</li> + * + * <li>The base password file: tpasswd. It contains the related password + * entries for all the users with values computed using SRP's default + * message digest algorithm: SHA-1 (with 160-bit output block size).</li> + * + * <li>The extended password file: tpasswd2. Its name, by default, is + * constructed by adding the suffix "2" to the fully qualified pathname of + * the base password file. It contains, in addition to the same fields as + * the base password file, albeit with a different verifier value, an extra + * field identifying the message digest algorithm used to compute this + * (verifier) value.</li> + * </ol></p> + * + * <p>This implementation assumes the following message digest algorithm codes: + * <ul> + * <li>0: the default hash algorithm, which is SHA-1 (or its alias SHA-160).</li> + * <li>1: MD5.</li> + * <li>2: RIPEMD-128.</li> + * <li>3: RIPEMD-160.</li> + * <li>4: SHA-256.</li> + * <li>5: SHA-384.</li> + * <li>6: SHA-512.</li> + * </ul></p> + * + * <p><b>IMPORTANT:</b> This method computes the verifiers as described in + * RFC-2945, which differs from the description given on the web page for + * SRP-6.</p> + * + * <p>Reference:</p> + * <ol> + * <li><a href="http://srp.stanford.edu/design.html">SRP Protocol Design</a><br> + * Thomas J. Wu.</li> + * </ol> + */ +public class PasswordFile +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // names of property keys used in this class + private static final String USER_FIELD = "user"; + + private static final String VERIFIERS_FIELD = "verifier"; + + private static final String SALT_FIELD = "salt"; + + private static final String CONFIG_FIELD = "config"; + + private static String DEFAULT_FILE; + static + { + DEFAULT_FILE = System.getProperty(SRPRegistry.PASSWORD_FILE, + SRPRegistry.DEFAULT_PASSWORD_FILE); + } + + /** The SRP algorithm instances used by this object. */ + private static final HashMap srps; + static + { + final HashMap map = new HashMap(SRPRegistry.SRP_ALGORITHMS.length); + // The first entry MUST exist. The others are optional. + map.put("0", SRP.instance(SRPRegistry.SRP_ALGORITHMS[0])); + for (int i = 1; i < SRPRegistry.SRP_ALGORITHMS.length; i++) + { + try + { + map.put(String.valueOf(i), + SRP.instance(SRPRegistry.SRP_ALGORITHMS[i])); + } + catch (Exception x) + { + System.err.println("Ignored: " + x); + x.printStackTrace(System.err); + } + } + srps = map; + } + + private String confName, pwName, pw2Name; + + private File configFile, passwdFile, passwd2File; + + private long lastmodPasswdFile, lastmodPasswd2File; + + private HashMap entries = new HashMap(); + + private HashMap configurations = new HashMap(); + + // default N values to use when creating a new password.conf file + private static final BigInteger[] Nsrp = new BigInteger[] { + SRPAlgorithm.N_2048, + SRPAlgorithm.N_1536, + SRPAlgorithm.N_1280, + SRPAlgorithm.N_1024, + SRPAlgorithm.N_768, + SRPAlgorithm.N_640, + SRPAlgorithm.N_512 }; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public PasswordFile() throws IOException + { + this(DEFAULT_FILE); + } + + public PasswordFile(final File pwFile) throws IOException + { + this(pwFile.getAbsolutePath()); + } + + public PasswordFile(final String pwName) throws IOException + { + this(pwName, pwName + "2", pwName + ".conf"); + } + + public PasswordFile(final String pwName, final String confName) + throws IOException + { + this(pwName, pwName + "2", confName); + } + + public PasswordFile(final String pwName, final String pw2Name, + final String confName) throws IOException + { + super(); + + this.pwName = pwName; + this.pw2Name = pw2Name; + this.confName = confName; + + readOrCreateConf(); + update(); + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns a string representing the decimal value of an integer + * identifying the message digest algorithm to use for the SRP computations. + * </p> + * + * @param mdName the canonical name of a message digest algorithm. + * @return a string representing the decimal value of an ID for that + * algorithm. + */ + private static final String nameToID(final String mdName) + { + if (Registry.SHA_HASH.equalsIgnoreCase(mdName) + || Registry.SHA1_HASH.equalsIgnoreCase(mdName) + || Registry.SHA160_HASH.equalsIgnoreCase(mdName)) + { + return "0"; + } + else if (Registry.MD5_HASH.equalsIgnoreCase(mdName)) + { + return "1"; + } + else if (Registry.RIPEMD128_HASH.equalsIgnoreCase(mdName)) + { + return "2"; + } + else if (Registry.RIPEMD160_HASH.equalsIgnoreCase(mdName)) + { + return "3"; + } + else if (Registry.SHA256_HASH.equalsIgnoreCase(mdName)) + { + return "4"; + } + else if (Registry.SHA384_HASH.equalsIgnoreCase(mdName)) + { + return "5"; + } + else if (Registry.SHA512_HASH.equalsIgnoreCase(mdName)) + { + return "6"; + } + return "0"; + } + + // SRP password configuration file methods --------------------------------- + + /** + * <p>Checks if the current configuration file contains the <N, g> pair + * for the designated <code>index</code>.</p> + * + * @param index a string representing 1-digit identification of an <N, g> + * pair used. + * @return <code>true</code> if the designated <code>index</code> is that of + * a known <N, g> pair, and <code>false</code> otherwise. + * @throws IOException if an exception occurs during the process. + * @see SRPRegistry#N_2048_BITS + * @see SRPRegistry#N_1536_BITS + * @see SRPRegistry#N_1280_BITS + * @see SRPRegistry#N_1024_BITS + * @see SRPRegistry#N_768_BITS + * @see SRPRegistry#N_640_BITS + * @see SRPRegistry#N_512_BITS + */ + public synchronized boolean containsConfig(final String index) + throws IOException + { + checkCurrent(); + return configurations.containsKey(index); + } + + /** + * <p>Returns a pair of strings representing the pair of <code>N</code> and + * <code>g</code> MPIs for the designated <code>index</code>.</p> + * + * @param index a string representing 1-digit identification of an <N, g> + * pair to look up. + * @return a pair of strings, arranged in an array, where the first (at index + * position #0) is the repesentation of the MPI <code>N</code>, and the + * second (at index position #1) is the representation of the MPI + * <code>g</code>. If the <code>index</code> refers to an unknown pair, then + * an empty string array is returned. + * @throws IOException if an exception occurs during the process. + */ + public synchronized String[] lookupConfig(final String index) + throws IOException + { + checkCurrent(); + String[] result = null; + if (configurations.containsKey(index)) + { + result = (String[]) configurations.get(index); + } + return result; + } + + // SRP base and extended password configuration files methods -------------- + + public synchronized boolean contains(final String user) throws IOException + { + checkCurrent(); + return entries.containsKey(user); + } + + public synchronized void add(final String user, final String passwd, + final byte[] salt, final String index) + throws IOException + { + checkCurrent(); + if (entries.containsKey(user)) + { + throw new UserAlreadyExistsException(user); + } + final HashMap fields = new HashMap(4); + fields.put(USER_FIELD, user); // 0 + fields.put(VERIFIERS_FIELD, newVerifiers(user, salt, passwd, index)); // 1 + fields.put(SALT_FIELD, Util.toBase64(salt)); // 2 + fields.put(CONFIG_FIELD, index); // 3 + entries.put(user, fields); + savePasswd(); + } + + public synchronized void changePasswd(final String user, final String passwd) + throws IOException + { + checkCurrent(); + if (!entries.containsKey(user)) + { + throw new NoSuchUserException(user); + } + final HashMap fields = (HashMap) entries.get(user); + final byte[] salt; + try + { + salt = Util.fromBase64((String) fields.get(SALT_FIELD)); + } + catch (NumberFormatException x) + { + throw new IOException("Password file corrupt"); + } + final String index = (String) fields.get(CONFIG_FIELD); + fields.put(VERIFIERS_FIELD, newVerifiers(user, salt, passwd, index)); + entries.put(user, fields); + savePasswd(); + } + + public synchronized void savePasswd() throws IOException + { + final FileOutputStream f1 = new FileOutputStream(passwdFile); + final FileOutputStream f2 = new FileOutputStream(passwd2File); + PrintWriter pw1 = null; + PrintWriter pw2 = null; + try + { + pw1 = new PrintWriter(f1, true); + pw2 = new PrintWriter(f2, true); + this.writePasswd(pw1, pw2); + } + finally + { + if (pw1 != null) + { + try + { + pw1.flush(); + } + finally + { + pw1.close(); + } + } + if (pw2 != null) + { + try + { + pw2.flush(); + } + finally + { + pw2.close(); + } + } + try + { + f1.close(); + } + catch (IOException ignored) + { + } + try + { + f2.close(); + } + catch (IOException ignored) + { + } + } + lastmodPasswdFile = passwdFile.lastModified(); + lastmodPasswd2File = passwd2File.lastModified(); + } + + /** + * <p>Returns the triplet: verifier, salt and configuration file index, of a + * designated user, and a designated message digest algorithm name, as an + * array of strings.</p> + * + * @param user the username. + * @param mdName the canonical name of the SRP's message digest algorithm. + * @return a string array containing, in this order, the BASE-64 encodings of + * the verifier, the salt and the index in the password configuration file of + * the MPIs N and g of the designated user. + */ + public synchronized String[] lookup(final String user, final String mdName) + throws IOException + { + checkCurrent(); + if (!entries.containsKey(user)) + { + throw new NoSuchUserException(user); + } + final HashMap fields = (HashMap) entries.get(user); + final HashMap verifiers = (HashMap) fields.get(VERIFIERS_FIELD); + final String salt = (String) fields.get(SALT_FIELD); + final String index = (String) fields.get(CONFIG_FIELD); + final String verifier = (String) verifiers.get(nameToID(mdName)); + return new String[] { verifier, salt, index }; + } + + // Other instance methods -------------------------------------------------- + + private synchronized void readOrCreateConf() throws IOException + { + configurations.clear(); + final FileInputStream fis; + configFile = new File(confName); + try + { + fis = new FileInputStream(configFile); + readConf(fis); + } + catch (FileNotFoundException x) + { // create a default one + final String g = Util.toBase64(Util.trim(new BigInteger("2"))); + String index, N; + for (int i = 0; i < Nsrp.length; i++) + { + index = String.valueOf(i + 1); + N = Util.toBase64(Util.trim(Nsrp[i])); + configurations.put(index, new String[] { N, g }); + } + FileOutputStream f0 = null; + PrintWriter pw0 = null; + try + { + f0 = new FileOutputStream(configFile); + pw0 = new PrintWriter(f0, true); + this.writeConf(pw0); + } + finally + { + if (pw0 != null) + { + pw0.close(); + } + else if (f0 != null) + { + f0.close(); + } + } + } + } + + private void readConf(final InputStream in) throws IOException + { + final BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line, index, N, g; + StringTokenizer st; + while ((line = din.readLine()) != null) + { + st = new StringTokenizer(line, ":"); + try + { + index = st.nextToken(); + N = st.nextToken(); + g = st.nextToken(); + } + catch (NoSuchElementException x) + { + throw new IOException("SRP password configuration file corrupt"); + } + configurations.put(index, new String[] { N, g }); + } + } + + private void writeConf(final PrintWriter pw) + { + String ndx; + String[] mpi; + StringBuffer sb; + for (Iterator it = configurations.keySet().iterator(); it.hasNext();) + { + ndx = (String) it.next(); + mpi = (String[]) configurations.get(ndx); + sb = new StringBuffer(ndx).append(":").append(mpi[0]).append(":").append( + mpi[1]); + pw.println(sb.toString()); + } + } + + /** + * <p>Compute the new verifiers for the designated username and password.</p> + * + * <p><b>IMPORTANT:</b> This method computes the verifiers as described in + * RFC-2945, which differs from the description given on the web page for + * SRP-6.</p> + * + * @param user the user's name. + * @param s the user's salt. + * @param password the user's password + * @param index the index of the <N, g> pair to use for this user. + * @return a {@link java.util.Map} of user verifiers. + * @throws UnsupportedEncodingException if the US-ASCII decoder is not + * available on this platform. + */ + private HashMap newVerifiers(final String user, final byte[] s, + final String password, final String index) + throws UnsupportedEncodingException + { + // to ensure inter-operability with non-java tools + final String[] mpi = (String[]) configurations.get(index); + final BigInteger N = new BigInteger(1, Util.fromBase64(mpi[0])); + final BigInteger g = new BigInteger(1, Util.fromBase64(mpi[1])); + + final HashMap result = new HashMap(srps.size()); + BigInteger x, v; + SRP srp; + for (int i = 0; i < srps.size(); i++) + { + final String digestID = String.valueOf(i); + srp = (SRP) srps.get(digestID); + x = new BigInteger(1, srp.computeX(s, user, password)); + v = g.modPow(x, N); + final String verifier = Util.toBase64(v.toByteArray()); + + result.put(digestID, verifier); + } + return result; + } + + private synchronized void update() throws IOException + { + entries.clear(); + + FileInputStream fis; + passwdFile = new File(pwName); + lastmodPasswdFile = passwdFile.lastModified(); + try + { + fis = new FileInputStream(passwdFile); + readPasswd(fis); + } + catch (FileNotFoundException ignored) + { + } + passwd2File = new File(pw2Name); + lastmodPasswd2File = passwd2File.lastModified(); + try + { + fis = new FileInputStream(passwd2File); + readPasswd2(fis); + } + catch (FileNotFoundException ignored) + { + } + } + + private void checkCurrent() throws IOException + { + if (passwdFile.lastModified() > lastmodPasswdFile + || passwd2File.lastModified() > lastmodPasswd2File) + { + update(); + } + } + + private void readPasswd(final InputStream in) throws IOException + { + final BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line, user, verifier, salt, index; + StringTokenizer st; + while ((line = din.readLine()) != null) + { + st = new StringTokenizer(line, ":"); + try + { + user = st.nextToken(); + verifier = st.nextToken(); + salt = st.nextToken(); + index = st.nextToken(); + } + catch (NoSuchElementException x) + { + throw new IOException("SRP base password file corrupt"); + } + + final HashMap verifiers = new HashMap(6); + verifiers.put("0", verifier); + + final HashMap fields = new HashMap(4); + fields.put(USER_FIELD, user); + fields.put(VERIFIERS_FIELD, verifiers); + fields.put(SALT_FIELD, salt); + fields.put(CONFIG_FIELD, index); + + entries.put(user, fields); + } + } + + private void readPasswd2(final InputStream in) throws IOException + { + final BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line, digestID, user, verifier; + StringTokenizer st; + HashMap fields, verifiers; + while ((line = din.readLine()) != null) + { + st = new StringTokenizer(line, ":"); + try + { + digestID = st.nextToken(); + user = st.nextToken(); + verifier = st.nextToken(); + } + catch (NoSuchElementException x) + { + throw new IOException("SRP extended password file corrupt"); + } + + fields = (HashMap) entries.get(user); + if (fields != null) + { + verifiers = (HashMap) fields.get(VERIFIERS_FIELD); + verifiers.put(digestID, verifier); + } + } + } + + private void writePasswd(final PrintWriter pw1, final PrintWriter pw2) + throws IOException + { + String user, digestID; + HashMap fields, verifiers; + StringBuffer sb1, sb2; + Iterator j; + final Iterator i = entries.keySet().iterator(); + while (i.hasNext()) + { + user = (String) i.next(); + fields = (HashMap) entries.get(user); + if (!user.equals(fields.get(USER_FIELD))) + { + throw new IOException("Inconsistent SRP password data"); + } + verifiers = (HashMap) fields.get(VERIFIERS_FIELD); + sb1 = new StringBuffer().append(user).append(":").append( + (String) verifiers.get("0")).append( + ":").append( + (String) fields.get(SALT_FIELD)).append( + ":").append( + (String) fields.get(CONFIG_FIELD)); + pw1.println(sb1.toString()); + // write extended information + j = verifiers.keySet().iterator(); + while (j.hasNext()) + { + digestID = (String) j.next(); + if (!"0".equals(digestID)) + { + // #0 is the default digest, already present in tpasswd! + sb2 = new StringBuffer().append(digestID).append(":").append( + user).append( + ":").append( + (String) verifiers.get(digestID)); + pw2.println(sb2.toString()); + } + } + } + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/SRP.java b/gnu/javax/crypto/sasl/srp/SRP.java new file mode 100644 index 000000000..d3eb596d4 --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/SRP.java @@ -0,0 +1,285 @@ +/* SRP.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.java.security.hash.HashFactory; +import gnu.java.security.hash.IMessageDigest; +import gnu.java.security.util.Util; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.HashMap; + +/** + * <p>A Factory class that returns SRP Singletons that know all SRP-related + * mathematical computations and protocol-related operations for both the + * client- and server-sides.</p> + */ +public final class SRP +{ + + // Constants and variables + // -------------------------------------------------------------------------- + + /** The map of already instantiated SRP algorithm instances. */ + private static final HashMap algorithms = new HashMap(); + + private static final byte COLON = (byte) 0x3A; + + /** The underlying message digest algorithm used for all SRP calculations. */ + private IMessageDigest mda; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** Trivial private constructor to enforce Singleton pattern. */ + private SRP(final IMessageDigest mda) + { + super(); + + this.mda = mda; + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns an instance of this object that uses the designated message + * digest algorithm as its digest function.</p> + * + * @return an instance of this object for the designated digest name. + */ + public static synchronized SRP instance(String mdName) + { + if (mdName != null) + { + mdName = mdName.trim().toLowerCase(); + } + if (mdName == null || mdName.equals("")) + { + mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME; + } + SRP result = (SRP) algorithms.get(mdName); + if (result == null) + { + final IMessageDigest mda = HashFactory.getInstance(mdName); + result = new SRP(mda); + algorithms.put(mdName, result); + } + return result; + } + + private static final byte[] xor(final byte[] b1, final byte[] b2, + final int length) + { + final byte[] result = new byte[length]; + for (int i = 0; i < length; ++i) + { + result[i] = (byte) (b1[i] ^ b2[i]); + } + return result; + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** @return the message digest algorithm name used by this instance. */ + public String getAlgorithm() + { + return mda.name(); + } + + // Message Digest algorithm related methods -------------------------------- + + /** + * <p>Returns a new instance of the SRP message digest algorithm --which is + * SHA-160 by default, but could be anything else provided the proper + * conditions as specified in the SRP specifications.</p> + * + * @return a new instance of the underlying SRP message digest algorithm. + * @throws RuntimeException if the implementation of the message digest + * algorithm does not support cloning. + */ + public IMessageDigest newDigest() + { + return (IMessageDigest) mda.clone(); + } + + /** + * <p>Convenience method to return the result of digesting the designated + * input with a new instance of the SRP message digest algorithm.</p> + * + * @param src some bytes to digest. + * @return the bytes constituting the result of digesting the designated + * input with a new instance of the SRP message digest algorithm. + */ + public byte[] digest(final byte[] src) + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + hash.update(src, 0, src.length); + return hash.digest(); + } + + /** + * <p>Convenience method to return the result of digesting the designated + * input with a new instance of the SRP message digest algorithm.</p> + * + * @param src a String whose bytes (using US-ASCII encoding) are to be + * digested. + * @return the bytes constituting the result of digesting the designated + * input with a new instance of the SRP message digest algorithm. + * @throws UnsupportedEncodingException if US-ASCII charset is not found. + */ + public byte[] digest(final String src) throws UnsupportedEncodingException + { + return digest(src.getBytes("US-ASCII")); + } + + // Other methods ----------------------------------------------------------- + + /** + * <p>Convenience method to XOR N bytes from two arrays; N being the output + * size of the SRP message digest algorithm.</p> + * + * @param a the first byte array. + * @param b the second one. + * @return N bytes which are the result of the XOR operations on the first N + * bytes from the designated arrays. N is the size of the SRP message digest + * algorithm; eg. 20 for SHA-160. + */ + public byte[] xor(final byte[] a, final byte[] b) + { + return xor(a, b, mda.hashSize()); + } + + public byte[] generateM1(final BigInteger N, final BigInteger g, + final String U, final byte[] s, final BigInteger A, + final BigInteger B, final byte[] K, final String I, + final String L, final byte[] cn, final byte[] cCB) + throws UnsupportedEncodingException + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + byte[] b; + b = xor(digest(Util.trim(N)), digest(Util.trim(g))); + hash.update(b, 0, b.length); + b = digest(U); + hash.update(b, 0, b.length); + hash.update(s, 0, s.length); + b = Util.trim(A); + hash.update(b, 0, b.length); + b = Util.trim(B); + hash.update(b, 0, b.length); + hash.update(K, 0, K.length); + b = digest(I); + hash.update(b, 0, b.length); + b = digest(L); + hash.update(b, 0, b.length); + hash.update(cn, 0, cn.length); + hash.update(cCB, 0, cCB.length); + + return hash.digest(); + } + + public byte[] generateM2(final BigInteger A, final byte[] M1, final byte[] K, + final String U, final String I, final String o, + final byte[] sid, final int ttl, final byte[] cIV, + final byte[] sIV, final byte[] sCB) + throws UnsupportedEncodingException + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + byte[] b; + b = Util.trim(A); + hash.update(b, 0, b.length); + hash.update(M1, 0, M1.length); + hash.update(K, 0, K.length); + b = digest(U); + hash.update(b, 0, b.length); + b = digest(I); + hash.update(b, 0, b.length); + b = digest(o); + hash.update(b, 0, b.length); + hash.update(sid, 0, sid.length); + hash.update((byte) (ttl >>> 24)); + hash.update((byte) (ttl >>> 16)); + hash.update((byte) (ttl >>> 8)); + hash.update((byte) ttl); + hash.update(cIV, 0, cIV.length); + hash.update(sIV, 0, sIV.length); + hash.update(sCB, 0, sCB.length); + + return hash.digest(); + } + + public byte[] generateKn(final byte[] K, final byte[] cn, final byte[] sn) + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + hash.update(K, 0, K.length); + hash.update(cn, 0, cn.length); + hash.update(sn, 0, sn.length); + + return hash.digest(); + } + + public byte[] computeX(final byte[] s, final String user, + final String password) + throws UnsupportedEncodingException + { + return computeX(s, user.getBytes("US-ASCII"), password.getBytes("US-ASCII")); + } + + public byte[] computeX(final byte[] s, final String user, final byte[] p) + throws UnsupportedEncodingException + { + return computeX(s, user.getBytes("US-ASCII"), p); + } + + private byte[] computeX(final byte[] s, final byte[] user, final byte[] p) + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + hash.update(user, 0, user.length); + hash.update(COLON); + hash.update(p, 0, p.length); + final byte[] up = hash.digest(); + + hash.update(s, 0, s.length); + hash.update(up, 0, up.length); + + return hash.digest(); + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/SRPAuthInfoProvider.java b/gnu/javax/crypto/sasl/srp/SRPAuthInfoProvider.java new file mode 100644 index 000000000..9ea21efb6 --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/SRPAuthInfoProvider.java @@ -0,0 +1,218 @@ +/* SRPAuthInfoProvider.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.java.security.util.Util; +import gnu.javax.crypto.sasl.IAuthInfoProvider; +import gnu.javax.crypto.sasl.NoSuchUserException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.security.sasl.AuthenticationException; + +/** + * <p>The SRP mechanism authentication information provider implementation.</p> + */ +public class SRPAuthInfoProvider implements IAuthInfoProvider +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private PasswordFile passwordFile = null; + + // Constructor(s) + // ------------------------------------------------------------------------- + + // implicit 0-args constrcutor + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // IAuthInfoProvider interface implementation ------------------------------ + + public void activate(Map context) throws AuthenticationException + { + try + { + if (context == null) + { + passwordFile = new PasswordFile(); + } + else + { + passwordFile = (PasswordFile) context.get(SRPRegistry.PASSWORD_DB); + if (passwordFile == null) + { + String pfn = (String) context.get(SRPRegistry.PASSWORD_FILE); + if (pfn == null) + { + passwordFile = new PasswordFile(); + } + else + { + passwordFile = new PasswordFile(pfn); + } + } + } + } + catch (IOException x) + { + throw new AuthenticationException("activate()", x); + } + } + + public void passivate() throws AuthenticationException + { + passwordFile = null; + } + + public boolean contains(String userName) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("contains()", + new IllegalStateException()); + } + boolean result = false; + try + { + result = passwordFile.contains(userName); + } + catch (IOException x) + { + throw new AuthenticationException("contains()", x); + } + return result; + } + + public Map lookup(Map userID) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("lookup()", + new IllegalStateException()); + } + Map result = new HashMap(); + try + { + String userName = (String) userID.get(Registry.SASL_USERNAME); + if (userName == null) + { + throw new NoSuchUserException(""); + } + String mdName = (String) userID.get(SRPRegistry.MD_NAME_FIELD); + + String[] data = passwordFile.lookup(userName, mdName); + result.put(SRPRegistry.USER_VERIFIER_FIELD, data[0]); + result.put(SRPRegistry.SALT_FIELD, data[1]); + result.put(SRPRegistry.CONFIG_NDX_FIELD, data[2]); + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + throw new AuthenticationException("lookup()", x); + } + return result; + } + + public void update(Map userCredentials) throws AuthenticationException + { + if (passwordFile == null) + throw new AuthenticationException("update()", new IllegalStateException()); + + try + { + String userName = (String) userCredentials.get(Registry.SASL_USERNAME); + String password = (String) userCredentials.get(Registry.SASL_PASSWORD); + String salt = (String) userCredentials.get(SRPRegistry.SALT_FIELD); + String config = (String) userCredentials.get(SRPRegistry.CONFIG_NDX_FIELD); + if (salt == null || config == null) + { + passwordFile.changePasswd(userName, password); + } + else + { + passwordFile.add(userName, password, Util.fromBase64(salt), config); + } + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + throw new AuthenticationException("update()", x); + } + } + + public Map getConfiguration(String mode) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("getConfiguration()", + new IllegalStateException()); + } + Map result = new HashMap(); + try + { + String[] data = passwordFile.lookupConfig(mode); + result.put(SRPRegistry.SHARED_MODULUS, data[0]); + result.put(SRPRegistry.FIELD_GENERATOR, data[1]); + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + throw new AuthenticationException("getConfiguration()", x); + } + return result; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/SRPClient.java b/gnu/javax/crypto/sasl/srp/SRPClient.java new file mode 100644 index 000000000..26a9e8aaf --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/SRPClient.java @@ -0,0 +1,1199 @@ +/* SRPClient.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.java.security.hash.MD5; +import gnu.java.security.util.Util; + +import gnu.javax.crypto.key.IKeyAgreementParty; +import gnu.javax.crypto.key.IncomingMessage; +import gnu.javax.crypto.key.KeyAgreementFactory; +import gnu.javax.crypto.key.KeyAgreementException; +import gnu.javax.crypto.key.OutgoingMessage; +import gnu.javax.crypto.key.srp6.SRP6KeyAgreement; +import gnu.javax.crypto.assembly.Direction; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.sasl.ClientMechanism; +import gnu.javax.crypto.sasl.IllegalMechanismStateException; +import gnu.javax.crypto.sasl.InputBuffer; +import gnu.javax.crypto.sasl.IntegrityException; +import gnu.javax.crypto.sasl.OutputBuffer; + +import gnu.javax.security.auth.Password; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashMap; +import java.util.StringTokenizer; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.DestroyFailedException; +import javax.security.sasl.AuthenticationException; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * <p>The SASL-SRP client-side mechanism.</p> + */ +public class SRPClient extends ClientMechanism implements SaslClient +{ + + // Debugging methods and variables + // ------------------------------------------------------------------------- + + private static final String NAME = "SRPClient"; + + // private static final String ERROR = "ERROR"; + // private static final String WARN = " WARN"; + private static final String INFO = " INFO"; + + private static final String TRACE = "DEBUG"; + + private static final boolean DEBUG = true; + + private static final int debuglevel = 3; + + private static final PrintWriter err = new PrintWriter(System.out, true); + + private static void debug(final String level, final Object obj) + { + err.println("[" + level + "] " + NAME + ": " + String.valueOf(obj)); + } + + // Constants and variables + // ------------------------------------------------------------------------- + + // private static final HashMap uid2ctx = new HashMap(); + + private String uid; // the unique key for this type of client + + private String U; // the authentication identity + + BigInteger N, g, A, B; + + private Password password; // the authentication credentials + + private byte[] s; // the user's salt + + private byte[] cIV, sIV; // client+server IVs, when confidentiality is on + + private byte[] M1, M2; // client+server evidences + + private byte[] cn, sn; // client's and server's nonce + + private SRP srp; // SRP algorithm instance used by this client + + private byte[] sid; // session ID when re-used + + private int ttl; // session time-to-live in seconds + + private byte[] sCB; // the peer's channel binding data + + private String L; // available options + + private String o; + + private String chosenIntegrityAlgorithm; + + private String chosenConfidentialityAlgorithm; + + private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT; + + private byte[] K; // shared session key + + private boolean replayDetection = true; // whether Replay Detection is on + + private int inCounter = 0; // messages sequence numbers + + private int outCounter = 0; + + private IALG inMac, outMac; // if !null, use for integrity + + private CALG inCipher, outCipher; // if !null, use for confidentiality + + private IKeyAgreementParty clientHandler = KeyAgreementFactory.getPartyAInstance(Registry.SRP_SASL_KA); + + // Constructor(s) + // ------------------------------------------------------------------------- + + public SRPClient() + { + super(Registry.SASL_SRP_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + // we shall keep track of the sid (and the security context of this + // SRP client) based on the initialisation parameters of an SRP session. + // we shall compute a unique key for those parameters and key the sid + // (and the security context) accordingly. + // 1. compute the mapping key. use MD5 (the fastest) for this purpose + final MD5 md = new MD5(); + byte[] b; + b = authorizationID.getBytes(); + md.update(b, 0, b.length); + b = serverName.getBytes(); + md.update(b, 0, b.length); + b = protocol.getBytes(); + md.update(b, 0, b.length); + if (channelBinding.length > 0) + { + md.update(channelBinding, 0, channelBinding.length); + } + uid = Util.toBase64(md.digest()); + if (ClientStore.instance().isAlive(uid)) + { + final SecurityContext ctx = ClientStore.instance().restoreSession(uid); + srp = SRP.instance(ctx.getMdName()); + sid = ctx.getSID(); + K = ctx.getK(); + cIV = ctx.getClientIV(); + sIV = ctx.getServerIV(); + replayDetection = ctx.hasReplayDetection(); + inCounter = ctx.getInCounter(); + outCounter = ctx.getOutCounter(); + inMac = ctx.getInMac(); + outMac = ctx.getOutMac(); + inCipher = ctx.getInCipher(); + outCipher = ctx.getOutCipher(); + } + else + { + sid = new byte[0]; + ttl = 0; + K = null; + cIV = null; + sIV = null; + cn = null; + sn = null; + } + } + + protected void resetMechanism() throws SaslException + { + try + { + password.destroy(); + } + catch (DestroyFailedException dfe) + { + SaslException se = new SaslException("resetMechanism()"); + se.initCause(dfe); + throw se; + } + password = null; + M1 = null; + K = null; + cIV = null; + sIV = null; + inMac = outMac = null; + inCipher = outCipher = null; + + sid = null; + ttl = 0; + cn = null; + sn = null; + } + + // javax.security.sasl.SaslClient interface implementation ----------------- + + public boolean hasInitialResponse() + { + return true; + } + + public byte[] evaluateChallenge(final byte[] challenge) throws SaslException + { + switch (state) + { + case 0: + state++; + return sendIdentities(); + case 1: + state++; + final byte[] result = sendPublicKey(challenge); + try + { + password.destroy(); //don't need further this session + } + catch (DestroyFailedException x) + { + SaslException se = new SaslException("sendPublicKey()"); + se.initCause(se); + throw se; + } + return result; + case 2: // should only occur if session re-use was rejected + if (!complete) + { + state++; + return receiveEvidence(challenge); + } + // else fall through + default: + throw new IllegalMechanismStateException("evaluateChallenge()"); + } + } + + protected byte[] engineUnwrap(final byte[] incoming, final int offset, + final int len) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> engineUnwrap()"); + + if (inMac == null && inCipher == null) + { + throw new IllegalStateException("connection is not protected"); + } + + // at this point one, or both, of confidentiality and integrity protection + // services are active. + + final byte[] result; + try + { + // final InputBuffer frameIn = InputBuffer.getInstance(incoming, offset, len); + // result = frameIn.getEOS(); + if (inMac != null) + { // integrity bytes are at the end of the stream + final int macBytesCount = inMac.length(); + final int payloadLength = len - macBytesCount; + // final byte[] received_mac = frameIn.getOS(); + final byte[] received_mac = new byte[macBytesCount]; + System.arraycopy(incoming, offset + payloadLength, received_mac, 0, + macBytesCount); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got C (received MAC): " + + Util.dumpString(received_mac)); + // inMac.update(result); + inMac.update(incoming, offset, payloadLength); + if (replayDetection) + { + inCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "inCounter=" + String.valueOf(inCounter)); + inMac.update(new byte[] { (byte) (inCounter >>> 24), + (byte) (inCounter >>> 16), + (byte) (inCounter >>> 8), + (byte) inCounter }); + } + + final byte[] computed_mac = inMac.doFinal(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Computed MAC: " + Util.dumpString(computed_mac)); + if (!Arrays.equals(received_mac, computed_mac)) + { + throw new IntegrityException("engineUnwrap()"); + } + + // deal with the payload, which can be either plain or encrypted + if (inCipher != null) + { + result = inCipher.doFinal(incoming, offset, payloadLength); + } + else + { + result = new byte[len - macBytesCount]; + System.arraycopy(incoming, offset, result, 0, result.length); + } + } + else + { // no integrity protection; just confidentiality + // if (inCipher != null) { + result = inCipher.doFinal(incoming, offset, len); + // } else { + // result = new byte[len]; + // System.arraycopy(incoming, offset, result, 0, len); + // } + } + // if (inCipher != null) { + // result = inCipher.doFinal(result); + // } + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("engineUnwrap()", x); + } + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== engineUnwrap()"); + return result; + } + + protected byte[] engineWrap(final byte[] outgoing, final int offset, + final int len) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> engineWrap()"); + + if (outMac == null && outCipher == null) + { + throw new IllegalStateException("connection is not protected"); + } + + // at this point one, or both, of confidentiality and integrity protection + // services are active. + + // byte[] data = new byte[len]; + // System.arraycopy(outgoing, offset, data, 0, len); + byte[] result; + try + { + // OutputBuffer frameOut = new OutputBuffer(); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + // Process the data + if (outCipher != null) + { + // data = outCipher.doFinal(data); + result = outCipher.doFinal(outgoing, offset, len); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding c (encrypted plaintext): " + + Util.dumpString(result)); + + // frameOut.setEOS(data); + out.write(result); + + if (outMac != null) + { + outMac.update(result); + if (replayDetection) + { + outCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "outCounter=" + String.valueOf(outCounter)); + outMac.update(new byte[] { (byte) (outCounter >>> 24), + (byte) (outCounter >>> 16), + (byte) (outCounter >>> 8), + (byte) outCounter }); + } + final byte[] C = outMac.doFinal(); + // frameOut.setOS(C); + out.write(C); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding C (integrity checksum): " + + Util.dumpString(C)); + } // else confidentiality only; do nothing + } + else + { // no confidentiality; just integrity [+ replay detection] + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding p (plaintext): "+Util.dumpString(data)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding p (plaintext): " + + Util.dumpString(outgoing, offset, len)); + + // frameOut.setEOS(data); + out.write(outgoing, offset, len); + + // if (outMac != null) { + // outMac.update(data); + outMac.update(outgoing, offset, len); + if (replayDetection) + { + outCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "outCounter=" + String.valueOf(outCounter)); + outMac.update(new byte[] { (byte) (outCounter >>> 24), + (byte) (outCounter >>> 16), + (byte) (outCounter >>> 8), + (byte) outCounter }); + } + final byte[] C = outMac.doFinal(); + // frameOut.setOS(C); + out.write(C); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding C (integrity checksum): " + + Util.dumpString(C)); + // } + } + + // frameOut.setEOS(data); + // + // if (outMac != null) { + // outMac.update(data); + // if (replayDetection) { + // outCounter++; + // if (DEBUG && debuglevel > 6) debug(TRACE, "outCounter="+String.valueOf(outCounter)); + // outMac.update(new byte[] { + // (byte)(outCounter >>> 24), + // (byte)(outCounter >>> 16), + // (byte)(outCounter >>> 8), + // (byte) outCounter }); + // } + // byte[] C = outMac.doFinal(); + // frameOut.setOS(C); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding C (integrity checksum): "+Util.dumpString(C)); + // } + + // result = frameOut.wrap(); + result = out.toByteArray(); + + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("engineWrap()", x); + } + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== engineWrap()"); + return result; + } + + protected String getNegotiatedQOP() + { + if (inMac != null) + { + if (inCipher != null) + { + return Registry.QOP_AUTH_CONF; + } + else + { + return Registry.QOP_AUTH_INT; + } + } + return Registry.QOP_AUTH; + } + + protected String getNegotiatedStrength() + { + if (inMac != null) + { + if (inCipher != null) + { + return Registry.STRENGTH_HIGH; + } + else + { + return Registry.STRENGTH_MEDIUM; + } + } + return Registry.STRENGTH_LOW; + } + + protected String getNegotiatedRawSendSize() + { + return String.valueOf(rawSendSize); + } + + protected String getReuse() + { + return Registry.REUSE_TRUE; + } + + // other methods ----------------------------------------------------------- + + private byte[] sendIdentities() throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> sendIdentities()"); + + // If necessary, prompt the client for the username and password + getUsernameAndPassword(); + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Password: \"" + new String(password.getPassword()) + "\""); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding U (username): \"" + U + "\""); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding I (userid): \"" + authorizationID + "\""); + + // if session re-use generate new 16-byte nonce + if (sid.length != 0) + { + cn = new SecureRandom ().generateSeed (16); + } + else + { + cn = new byte[0]; + } + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setText(U); + frameOut.setText(authorizationID); + frameOut.setEOS(sid); // session ID to re-use + frameOut.setOS(cn); // client nonce + frameOut.setEOS(channelBinding); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendIdentities()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendIdentities()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "C: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " U = " + U); + if (DEBUG && debuglevel > 2) + debug(INFO, " I = " + authorizationID); + if (DEBUG && debuglevel > 2) + debug(INFO, "sid = " + new String(sid)); + if (DEBUG && debuglevel > 2) + debug(INFO, " cn = " + Util.dumpString(cn)); + if (DEBUG && debuglevel > 2) + debug(INFO, "cCB = " + Util.dumpString(channelBinding)); + return result; + } + + private byte[] sendPublicKey(final byte[] input) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> sendPublicKey()"); + if (DEBUG && debuglevel > 6) + debug(TRACE, "S: " + Util.dumpString(input)); + + // Server sends [00], N, g, s, B, L + // or [FF], sn, sCB + final InputBuffer frameIn = new InputBuffer(input); + final int ack; + try + { + ack = (int) frameIn.getScalar(1); + if (ack == 0x00) + { // new session + N = frameIn.getMPI(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got N (modulus): " + Util.dump(N)); + g = frameIn.getMPI(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got g (generator): " + Util.dump(g)); + s = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got s (salt): " + Util.dumpString(s)); + B = frameIn.getMPI(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got B (server ephermeral public key): " + + Util.dump(B)); + L = frameIn.getText(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got L (available options): \"" + L + "\""); + } + else if (ack == 0xFF) + { // session re-use + sn = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sn (server nonce): " + Util.dumpString(sn)); + sCB = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sCB (server channel binding): " + + Util.dumpString(sCB)); + } + else + { // unexpected scalar + throw new SaslException("sendPublicKey(): Invalid scalar (" + ack + + ") in server's request"); + } + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("sendPublicKey()", x); + } + + if (ack == 0x00) + { // new session --------------------------------------- + o = createO(L.toLowerCase()); // do this first to initialise the SRP hash + + final byte[] pBytes; // use ASCII encoding to inter-operate w/ non-java + pBytes = password.getBytes(); + + // ---------------------------------------------------------------------- + final HashMap mapA = new HashMap(); + // mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.newDigest()); + mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm()); + mapA.put(SRP6KeyAgreement.USER_IDENTITY, U); + mapA.put(SRP6KeyAgreement.USER_PASSWORD, pBytes); + try + { + clientHandler.init(mapA); + clientHandler.processMessage(null); + } + catch (KeyAgreementException x) + { + throw new SaslException("sendPublicKey()", x); + } + + // ---------------------------------------------------------------------- + + // ------------------------------------------------------------------- + try + { + OutgoingMessage out = new OutgoingMessage(); + out.writeMPI(N); + out.writeMPI(g); + out.writeMPI(new BigInteger(1, s)); + out.writeMPI(B); + IncomingMessage in = new IncomingMessage(out.toByteArray()); + out = clientHandler.processMessage(in); + + in = new IncomingMessage(out.toByteArray()); + A = in.readMPI(); + K = clientHandler.getSharedSecret(); + } + catch (KeyAgreementException x) + { + throw new SaslException("sendPublicKey()", x); + } + // ------------------------------------------------------------------- + + if (DEBUG && debuglevel > 6) + debug(TRACE, "K: " + Util.dumpString(K)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding A (client ephemeral public key): " + + Util.dump(A)); + + try + { + M1 = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn, + channelBinding); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("sendPublicKey()", x); + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding o (client chosen options): \"" + o + "\""); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding cIV (client IV): \"" + Util.dumpString(cIV) + + "\""); + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setMPI(A); + frameOut.setOS(M1); + frameOut.setText(o); + frameOut.setOS(cIV); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendPublicKey()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendPublicKey()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "New session, or session re-use rejected..."); + if (DEBUG && debuglevel > 2) + debug(INFO, "C: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " A = 0x" + A.toString(16)); + if (DEBUG && debuglevel > 2) + debug(INFO, " M1 = " + Util.dumpString(M1)); + if (DEBUG && debuglevel > 2) + debug(INFO, " o = " + o); + if (DEBUG && debuglevel > 2) + debug(INFO, "cIV = " + Util.dumpString(cIV)); + + return result; + } + else + { // session re-use accepted ------------------------------------- + setupSecurityServices(true); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendPublicKey()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "Session re-use accepted..."); + return null; + } + } + + private byte[] receiveEvidence(byte[] input) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> receiveEvidence()"); + if (DEBUG && debuglevel > 6) + debug(TRACE, "S: " + Util.dumpString(input)); + + // Server send M2, sIV, sCB, sid, ttl + final InputBuffer frameIn = new InputBuffer(input); + try + { + M2 = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got M2 (server evidence): " + Util.dumpString(M2)); + sIV = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sIV (server IV): " + Util.dumpString(sIV)); + sid = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sid (session ID): " + new String(sid)); + ttl = (int) frameIn.getScalar(4); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got ttl (session time-to-live): " + ttl + "sec."); + sCB = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sCB (server channel binding): " + + Util.dumpString(sCB)); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("receiveEvidence()", x); + } + + final byte[] expected; + try + { + expected = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl, + cIV, sIV, sCB); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("receiveEvidence()", x); + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Expected: " + Util.dumpString(expected)); + if (!Arrays.equals(M2, expected)) + { + throw new AuthenticationException("M2 mismatch"); + } + + setupSecurityServices(false); + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== receiveEvidence()"); + return null; + } + + private void getUsernameAndPassword() throws AuthenticationException + { + try + { + if ((!properties.containsKey(Registry.SASL_USERNAME)) + && (!properties.containsKey(Registry.SASL_PASSWORD))) + { + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + { + nameCB = new NameCallback("username: "); + } + else + { + nameCB = new NameCallback("username: ", defaultName); + } + final PasswordCallback pwdCB = new PasswordCallback("password: ", + false); + handler.handle(new Callback[] { nameCB, pwdCB }); + U = nameCB.getName(); + password = new Password(pwdCB.getPassword()); + } + else + { + if (properties.containsKey(Registry.SASL_USERNAME)) + { + this.U = (String) properties.get(Registry.SASL_USERNAME); + } + else + { + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + { + nameCB = new NameCallback("username: "); + } + else + { + nameCB = new NameCallback("username: ", defaultName); + } + this.handler.handle(new Callback[] { nameCB }); + this.U = nameCB.getName(); + } + + if (properties.containsKey(Registry.SASL_PASSWORD)) + { + Object pw = properties.get(Registry.SASL_PASSWORD); + if (pw instanceof char[]) + password = new Password((char[]) pw); + else if (pw instanceof Password) + password = (Password) pw; + else if (pw instanceof String) + password = new Password(((String) pw).toCharArray()); + else + throw new IllegalArgumentException( + pw.getClass().getName() + + "is not a valid password class"); + } + else + { + final PasswordCallback pwdCB = new PasswordCallback( + "password: ", + false); + this.handler.handle(new Callback[] { pwdCB }); + password = new Password(pwdCB.getPassword()); + } + } + + if (U == null) + { + throw new AuthenticationException("null username supplied"); + } + if (password == null) + { + throw new AuthenticationException("null password supplied"); + } + } + catch (UnsupportedCallbackException x) + { + throw new AuthenticationException("getUsernameAndPassword()", x); + } + catch (IOException x) + { + throw new AuthenticationException("getUsernameAndPassword()", x); + } + } + + // We go through the list of available services and for each available one + // we decide whether or not we want it enabled, based on properties passed + // to us by the client. + private String createO(final String aol) throws AuthenticationException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> createO(\"" + aol + "\")"); + + boolean replaydetectionAvailable = false; + boolean integrityAvailable = false; + boolean confidentialityAvailable = false; + String option, mandatory = SRPRegistry.DEFAULT_MANDATORY; + int i; + + String mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME; + + final StringTokenizer st = new StringTokenizer(aol, ","); + while (st.hasMoreTokens()) + { + option = st.nextToken(); + if (option.startsWith(SRPRegistry.OPTION_SRP_DIGEST + "=")) + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "mda: <" + option + ">"); + for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.SRP_ALGORITHMS[i].equals(option)) + { + mdName = option; + break; + } + } + } + else if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION)) + { + replaydetectionAvailable = true; + } + else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "=")) + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "ialg: <" + option + ">"); + for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option)) + { + chosenIntegrityAlgorithm = option; + integrityAvailable = true; + break; + } + } + } + else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "=")) + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "calg: <" + option + ">"); + for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option)) + { + chosenConfidentialityAlgorithm = option; + confidentialityAvailable = true; + break; + } + } + } + else if (option.startsWith(SRPRegistry.OPTION_MANDATORY + "=")) + { + mandatory = option.substring(option.indexOf('=') + 1); + } + else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=")) + { + final String maxBufferSize = option.substring(option.indexOf('=') + 1); + try + { + rawSendSize = Integer.parseInt(maxBufferSize); + if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT + || rawSendSize < 1) + { + throw new AuthenticationException( + "Illegal value for 'maxbuffersize' option"); + } + } + catch (NumberFormatException x) + { + throw new AuthenticationException( + SRPRegistry.OPTION_MAX_BUFFER_SIZE + + "=" + + String.valueOf(maxBufferSize), + x); + } + } + } + + replayDetection = replaydetectionAvailable + && Boolean.valueOf( + (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION)).booleanValue(); + boolean integrity = integrityAvailable + && Boolean.valueOf( + (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION)).booleanValue(); + boolean confidentiality = confidentialityAvailable + && Boolean.valueOf( + (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY)).booleanValue(); + + // make sure we do the right thing + if (SRPRegistry.OPTION_REPLAY_DETECTION.equals(mandatory)) + { + replayDetection = true; + integrity = true; + } + else if (SRPRegistry.OPTION_INTEGRITY.equals(mandatory)) + { + integrity = true; + } + else if (SRPRegistry.OPTION_CONFIDENTIALITY.equals(mandatory)) + { + confidentiality = true; + } + if (replayDetection) + { + if (chosenIntegrityAlgorithm == null) + { + throw new AuthenticationException( + "Replay detection is required but no " + + "integrity protection algorithm was chosen"); + } + } + if (integrity) + { + if (chosenIntegrityAlgorithm == null) + { + throw new AuthenticationException( + "Integrity protection is required but no " + + "algorithm was chosen"); + } + } + if (confidentiality) + { + if (chosenConfidentialityAlgorithm == null) + { + throw new AuthenticationException( + "Confidentiality protection is required " + + "but no algorithm was chosen"); + } + } + + // 1. check if we'll be using confidentiality; if not set IV to 0-byte + if (chosenConfidentialityAlgorithm == null) + { + cIV = new byte[0]; + } + else + { + // 2. get the block size of the cipher + final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm); + if (cipher == null) + { + throw new AuthenticationException("createO()", + new NoSuchAlgorithmException()); + } + final int blockSize = cipher.defaultBlockSize(); + // 3. generate random iv + cIV = new byte[blockSize]; + new SecureRandom ().nextBytes(cIV); + } + + srp = SRP.instance(mdName); + + // Now create the options list specifying which of the available options + // we have chosen. + + // For now we just select the defaults. Later we need to add support for + // properties (perhaps in a file) where a user can specify the list of + // algorithms they would prefer to use. + + final StringBuffer sb = new StringBuffer(); + sb.append(SRPRegistry.OPTION_SRP_DIGEST).append("=").append(mdName).append( + ","); + if (replayDetection) + { + sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(","); + } + if (integrity) + { + sb.append(SRPRegistry.OPTION_INTEGRITY).append("=").append( + chosenIntegrityAlgorithm).append( + ","); + } + if (confidentiality) + { + sb.append(SRPRegistry.OPTION_CONFIDENTIALITY).append("=").append( + chosenConfidentialityAlgorithm).append( + ","); + } + final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE).append( + "=").append( + Registry.SASL_BUFFER_MAX_LIMIT).toString(); + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== createO() --> " + result); + return result; + } + + private void setupSecurityServices(final boolean sessionReUse) + throws SaslException + { + complete = true; // signal end of authentication phase + if (!sessionReUse) + { + outCounter = inCounter = 0; + // instantiate cipher if confidentiality protection filter is active + if (chosenConfidentialityAlgorithm != null) + { + if (DEBUG && debuglevel > 2) + debug(INFO, "Activating confidentiality protection filter"); + inCipher = CALG.getInstance(chosenConfidentialityAlgorithm); + outCipher = CALG.getInstance(chosenConfidentialityAlgorithm); + } + // instantiate hmacs if integrity protection filter is active + if (chosenIntegrityAlgorithm != null) + { + if (DEBUG && debuglevel > 2) + debug(INFO, "Activating integrity protection filter"); + inMac = IALG.getInstance(chosenIntegrityAlgorithm); + outMac = IALG.getInstance(chosenIntegrityAlgorithm); + } + } + else + { // same session new Keys + K = srp.generateKn(K, cn, sn); + } + + final KDF kdf = KDF.getInstance(K); + + // initialise in/out ciphers if confidentiality protection is used + if (inCipher != null) + { + inCipher.init(kdf, sIV, Direction.REVERSED); + outCipher.init(kdf, cIV, Direction.FORWARD); + } + // initialise in/out macs if integrity protection is used + if (inMac != null) + { + inMac.init(kdf); + outMac.init(kdf); + } + + if (sid != null && sid.length != 0) + { // update the security context and save in map + if (DEBUG && debuglevel > 2) + debug(INFO, "Updating security context for UID = " + uid); + ClientStore.instance().cacheSession( + uid, + ttl, + new SecurityContext( + srp.getAlgorithm(), + sid, + K, + cIV, + sIV, + replayDetection, + inCounter, + outCounter, + inMac, outMac, + inCipher, + outCipher)); + } + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/SRPRegistry.java b/gnu/javax/crypto/sasl/srp/SRPRegistry.java new file mode 100644 index 000000000..262cbcba3 --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/SRPRegistry.java @@ -0,0 +1,219 @@ +/* SRPRegistry.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; + +/** + * <p>A list of key names designating the values exchanged between the server + * and client in an SRP communication authentication phase.</p> + */ +public interface SRPRegistry +{ + + /** Indices of (N, g) parameter values for SRP (.conf) password database. */ + String N_2048_BITS = "1"; + + String N_1536_BITS = "2"; + + String N_1280_BITS = "3"; + + String N_1024_BITS = "4"; + + String N_768_BITS = "5"; + + String N_640_BITS = "6"; + + String N_512_BITS = "7"; + + /** Available hash algorithms for all SRP calculations. */ + String[] SRP_ALGORITHMS = { Registry.SHA160_HASH, // the default one + Registry.MD5_HASH, Registry.RIPEMD128_HASH, + Registry.RIPEMD160_HASH, + + Registry.SHA256_HASH, Registry.SHA384_HASH, + Registry.SHA512_HASH }; + + /** + * The name of the default message digest algorithm to use when no name is + * explicitely given. In this implementation it is the <b>first</b> among + * those supported; i.e. the algorithm at index position #0: SHA with + * 160-bit output. + */ + String SRP_DEFAULT_DIGEST_NAME = SRP_ALGORITHMS[0]; + + /** + * The property name of the message digest algorithm name to use in a given + * SRP incarnation. + */ + String SRP_DIGEST_NAME = "srp.digest.name"; + + /** The public shared modulus: n. */ + String SHARED_MODULUS = "srp.N"; + + /** The GF generator used: g. */ + String FIELD_GENERATOR = "srp.g"; + + /** The list of server's available security options. */ + String AVAILABLE_OPTIONS = "srp.L"; + + /** The client's chosen security options. */ + String CHOSEN_OPTIONS = "srp.o"; + + /** The client's username. */ + String USER_NAME = "srp.U"; + + /** The client's authorization ID. */ + String USER_ROLE = "srp.I"; + + /** The user's salt. */ + String USER_SALT = "srp.s"; + + /** The user's password verifier. */ + String PASSWORD_VERIFIER = "srp.v"; + + /** The client's public ephemeral exponent: A. */ + String CLIENT_PUBLIC_KEY = "srp.A"; + + /** The server's public ephemeral exponent: B. */ + String SERVER_PUBLIC_KEY = "srp.B"; + + /** The client's evidence: M1. */ + String CLIENT_EVIDENCE = "srp.M1"; + + /** The server's evidence: M2. */ + String SERVER_EVIDENCE = "srp.M2"; + + /** Name of underlying hash algorithm for use with all SRP calculations. */ + String SRP_HASH = "gnu.crypto.sasl.srp.hash"; + + /** Name of SRP mandatory service property. */ + String SRP_MANDATORY = "gnu.crypto.sasl.srp.mandatory"; + + /** Name of SRP replay detection property. */ + String SRP_REPLAY_DETECTION = "gnu.crypto.sasl.srp.replay.detection"; + + /** Name of SRP integrity protection property. */ + String SRP_INTEGRITY_PROTECTION = "gnu.crypto.sasl.srp.integrity"; + + /** Name of SRP confidentiality protection property. */ + String SRP_CONFIDENTIALITY = "gnu.crypto.sasl.srp.confidentiality"; + + /** Name of the main SRP password file pathname property. */ + String PASSWORD_FILE = "gnu.crypto.sasl.srp.password.file"; + + /** + * Name of the SRP password database property --a reference to + * {@link gnu.crypto.sasl.srp.PasswordFile} object. + */ + String PASSWORD_DB = "gnu.crypto.sasl.srp.password.db"; + + /** Default fully qualified pathname of the SRP password file. */ + String DEFAULT_PASSWORD_FILE = "/etc/tpasswd"; + + /** Default value for replay detection security service. */ + boolean DEFAULT_REPLAY_DETECTION = true; + + /** Default value for integrity protection security service. */ + boolean DEFAULT_INTEGRITY = true; // implied by the previous option + + /** Default value for confidentiality protection security service. */ + boolean DEFAULT_CONFIDENTIALITY = false; + + // constants defining HMAC names + String HMAC_SHA1 = "hmac-sha1"; + + String HMAC_MD5 = "hmac-md5"; + + String HMAC_RIPEMD_160 = "hmac-ripemd-160"; + + /** Available HMAC algorithms for integrity protection. */ + String[] INTEGRITY_ALGORITHMS = { HMAC_SHA1, HMAC_MD5, HMAC_RIPEMD_160 }; + + // constants defining Cipher names + String AES = "aes"; + + String BLOWFISH = "blowfish"; + + /** Available Cipher algorithms for confidentiality protection. */ + String[] CONFIDENTIALITY_ALGORITHMS = { AES, BLOWFISH }; + + /** String for mandatory replay detection. */ + String OPTION_MANDATORY = "mandatory"; + + /** String for mda: the SRP digest algorithm name. */ + String OPTION_SRP_DIGEST = "mda"; + + /** String for mandatory replay detection. */ + String OPTION_REPLAY_DETECTION = "replay_detection"; + + /** String for mandatory integrity protection. */ + String OPTION_INTEGRITY = "integrity"; + + /** String for mandatory confidentiality protection. */ + String OPTION_CONFIDENTIALITY = "confidentiality"; + + /** String for mandatory replay detection. */ + String OPTION_MAX_BUFFER_SIZE = "maxbuffersize"; + + /** String for no mandatory security service. */ + String MANDATORY_NONE = "none"; + + /** Default mandatory security service required. */ + // String DEFAULT_MANDATORY = MANDATORY_NONE; + String DEFAULT_MANDATORY = OPTION_REPLAY_DETECTION; + + // String DEFAULT_MANDATORY = OPTION_INTEGRITY; + // String DEFAULT_MANDATORY = OPTION_CONFIDENTIALITY; + + /** Name of the UID field in the plain password file. */ + String MD_NAME_FIELD = "srp.md.name"; + + /** Name of the GID field in the plain password file. */ + String USER_VERIFIER_FIELD = "srp.user.verifier"; + + /** Name of the GECOS field in the plain password file. */ + String SALT_FIELD = "srp.salt"; + + /** Name of the SHELL field in the plain password file. */ + String CONFIG_NDX_FIELD = "srp.config.ndx"; + + /** Minimum bitlength of the SRP public modulus. */ + int MINIMUM_MODULUS_BITLENGTH = 512; +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/SRPServer.java b/gnu/javax/crypto/sasl/srp/SRPServer.java new file mode 100644 index 000000000..421da6e1b --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/SRPServer.java @@ -0,0 +1,1147 @@ +/* SRPServer.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.java.security.util.Util; + +import gnu.javax.crypto.assembly.Direction; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.key.IKeyAgreementParty; +import gnu.javax.crypto.key.KeyAgreementFactory; +import gnu.javax.crypto.key.KeyAgreementException; +import gnu.javax.crypto.key.OutgoingMessage; +import gnu.javax.crypto.key.IncomingMessage; +import gnu.javax.crypto.key.srp6.SRP6KeyAgreement; +import gnu.javax.crypto.sasl.IllegalMechanismStateException; +import gnu.javax.crypto.sasl.InputBuffer; +import gnu.javax.crypto.sasl.IntegrityException; +import gnu.javax.crypto.sasl.OutputBuffer; +import gnu.javax.crypto.sasl.ServerMechanism; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashMap; +import java.util.StringTokenizer; + +import javax.security.sasl.AuthenticationException; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +/** + * <p>The SASL-SRP server-side mechanism.</p> + * + * @version $Revision: 1.1 $ + */ +public class SRPServer extends ServerMechanism implements SaslServer +{ + + // Debugging methods and variables + // ------------------------------------------------------------------------- + + private static final String NAME = "SRPServer"; + + // private static final String ERROR = "ERROR"; + private static final String WARN = " WARN"; + + private static final String INFO = " INFO"; + + private static final String TRACE = "DEBUG"; + + private static final boolean DEBUG = true; + + private static final int debuglevel = 3; + + private static final PrintWriter err = new PrintWriter(System.out, true); + + private static void debug(final String level, final Object obj) + { + err.println("[" + level + "] " + NAME + ": " + String.valueOf(obj)); + } + + // Constants and variables + // ------------------------------------------------------------------------- + + private String U = null; // client's username + + private BigInteger N, g, A, B; + + private byte[] s; // salt + + private byte[] cIV, sIV; // client+server IVs, when confidentiality is on + + private byte[] cn, sn; // client's and server's nonce + + private SRP srp; // SRP algorithm instance used by this server + + private byte[] sid; // session ID when re-used + + private int ttl = 360; // session time-to-live in seconds + + private byte[] cCB; // peer's channel binding' + + private String mandatory; // List of available options + + private String L = null; + + private String o; + + private String chosenIntegrityAlgorithm; + + private String chosenConfidentialityAlgorithm; + + private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT; + + private byte[] K; // shared session key + + private boolean replayDetection = true; // whether Replay Detection is on + + private int inCounter = 0; // messages sequence numbers + + private int outCounter = 0; + + private IALG inMac, outMac; // if !null, use for integrity + + private CALG inCipher, outCipher; // if !null, use for confidentiality + + private IKeyAgreementParty serverHandler = KeyAgreementFactory.getPartyBInstance(Registry.SRP_SASL_KA); + + // Constructor(s) + // ------------------------------------------------------------------------- + + public SRPServer() + { + super(Registry.SASL_SRP_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + // TODO: + // we must have a means to map a given username to a preferred + // SRP hash algorithm; otherwise we end up using _always_ SHA. + // for the time being get it from the mechanism properties map + // and apply it for all users. + final String mda = (String) properties.get(SRPRegistry.SRP_HASH); + srp = SRP.instance(mda == null ? SRPRegistry.SRP_DEFAULT_DIGEST_NAME : mda); + } + + protected void resetMechanism() throws SaslException + { + s = null; + A = B = null; + K = null; + inMac = outMac = null; + inCipher = outCipher = null; + + sid = null; + } + + // javax.security.sasl.SaslServer interface implementation ----------------- + + public byte[] evaluateResponse(final byte[] response) throws SaslException + { + switch (state) + { + case 0: + if (response == null) + { + return null; + } + state++; + return sendProtocolElements(response); + case 1: + if (!complete) + { + state++; + return sendEvidence(response); + } + // else fall through + default: + throw new IllegalMechanismStateException("evaluateResponse()"); + } + } + + protected byte[] engineUnwrap(final byte[] incoming, final int offset, + final int len) throws SaslException + { + // if (DEBUG && debuglevel > 8) debug(TRACE, "==> engineUnwrap()"); + // + // if (inMac == null && inCipher == null) { + // throw new IllegalStateException("connection is not protected"); + // } + // + // if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (before security): "+Util.dumpString(incoming, offset, len)); + // + // byte[] data = null; + // try { + // InputBuffer frameIn = InputBuffer.getInstance(incoming, offset, len); + // data = frameIn.getEOS(); + // if (inMac != null) { + // byte[] received_mac = frameIn.getOS(); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Got C (received MAC): "+Util.dumpString(received_mac)); + // inMac.update(data); + // if (replayDetection) { + // inCounter++; + // if (DEBUG && debuglevel > 6) debug(TRACE, "inCounter="+String.valueOf(inCounter)); + // inMac.update(new byte[] { + // (byte)(inCounter >>> 24), + // (byte)(inCounter >>> 16), + // (byte)(inCounter >>> 8), + // (byte) inCounter }); + // } + // final byte[] computed_mac = inMac.doFinal(); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Computed MAC: "+Util.dumpString(computed_mac)); + // if (!Arrays.equals(received_mac, computed_mac)) + // throw new IntegrityException("engineUnwrap()"); + // } + // if (inCipher != null) { + // data = inCipher.doFinal(data); + // } + // } catch (IOException x) { + // if (x instanceof SaslException) { + // throw (SaslException) x; + // } + // throw new SaslException("engineUnwrap()", x); + // } + // + // if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (after security): "+Util.dumpString(data)); + // if (DEBUG && debuglevel > 8) debug(TRACE, "<== engineUnwrap()"); + // return data; + + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> engineUnwrap()"); + + if (inMac == null && inCipher == null) + { + throw new IllegalStateException("connection is not protected"); + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Incoming buffer (before security): " + + Util.dumpString(incoming, offset, len)); + + // at this point one, or both, of confidentiality and integrity protection + // services are active. + + final byte[] result; + try + { + if (inMac != null) + { // integrity bytes are at the end of the stream + final int macBytesCount = inMac.length(); + final int payloadLength = len - macBytesCount; + final byte[] received_mac = new byte[macBytesCount]; + System.arraycopy(incoming, offset + payloadLength, received_mac, 0, + macBytesCount); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got C (received MAC): " + + Util.dumpString(received_mac)); + inMac.update(incoming, offset, payloadLength); + if (replayDetection) + { + inCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "inCounter=" + String.valueOf(inCounter)); + inMac.update(new byte[] { (byte) (inCounter >>> 24), + (byte) (inCounter >>> 16), + (byte) (inCounter >>> 8), + (byte) inCounter }); + } + + final byte[] computed_mac = inMac.doFinal(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Computed MAC: " + Util.dumpString(computed_mac)); + if (!Arrays.equals(received_mac, computed_mac)) + { + throw new IntegrityException("engineUnwrap()"); + } + + // deal with the payload, which can be either plain or encrypted + if (inCipher != null) + { + result = inCipher.doFinal(incoming, offset, payloadLength); + } + else + { + result = new byte[payloadLength]; + System.arraycopy(incoming, offset, result, 0, result.length); + } + } + else + { // no integrity protection; just confidentiality + // if (inCipher != null) { + result = inCipher.doFinal(incoming, offset, len); + // } else { + // result = new byte[len]; + // System.arraycopy(incoming, offset, result, 0, len); + // } + } + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("engineUnwrap()", x); + } + if (DEBUG && debuglevel > 6) + debug(TRACE, "Incoming buffer (after security): " + + Util.dumpString(result)); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== engineUnwrap()"); + return result; + } + + protected byte[] engineWrap(final byte[] outgoing, final int offset, + final int len) throws SaslException + { + // if (DEBUG && debuglevel > 8) debug(TRACE, "==> engineWrap()"); + // + // if (outMac == null && outCipher == null) { + // throw new IllegalStateException("connection is not protected"); + // } + // + // byte[] data = new byte[len]; + // System.arraycopy(outgoing, offset, data, 0, len); + // + // if (DEBUG && debuglevel > 6) debug(TRACE, "Outgoing buffer (before security) (hex): "+Util.dumpString(data)); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Outgoing buffer (before security) (str): \""+new String(data)+"\""); + // + // final byte[] result; + // try { + // OutputBuffer frameOut = new OutputBuffer(); + // // Process the data + // if (outCipher != null) { + // data = outCipher.doFinal(data); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding c (encrypted plaintext): "+Util.dumpString(data)); + // } else { + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding p (plaintext): "+Util.dumpString(data)); + // } + // frameOut.setEOS(data); + // if (outMac != null) { + // outMac.update(data); + // if (replayDetection) { + // outCounter++; + // if (DEBUG && debuglevel > 6) debug(TRACE, "outCounter="+String.valueOf(outCounter)); + // outMac.update(new byte[] { + // (byte)(outCounter >>> 24), + // (byte)(outCounter >>> 16), + // (byte)(outCounter >>> 8), + // (byte) outCounter}); + // } + // byte[] C = outMac.doFinal(); + // frameOut.setOS(C); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding C (integrity checksum): "+Util.dumpString(C)); + // } + // result = frameOut.wrap(); + // + // } catch (IOException x) { + // if (x instanceof SaslException) { + // throw (SaslException) x; + // } + // throw new SaslException("engineWrap()", x); + // } + // + // if (DEBUG && debuglevel > 8) debug(TRACE, "<== engineWrap()"); + // return result; + + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> engineWrap()"); + + if (outMac == null && outCipher == null) + { + throw new IllegalStateException("connection is not protected"); + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Outgoing buffer (before security) (hex): " + + Util.dumpString(outgoing, offset, len)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Outgoing buffer (before security) (str): \"" + + new String(outgoing, offset, len) + "\""); + + // at this point one, or both, of confidentiality and integrity protection + // services are active. + + byte[] result; + try + { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + if (outCipher != null) + { + result = outCipher.doFinal(outgoing, offset, len); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding c (encrypted plaintext): " + + Util.dumpString(result)); + + out.write(result); + + if (outMac != null) + { + outMac.update(result); + if (replayDetection) + { + outCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "outCounter=" + String.valueOf(outCounter)); + outMac.update(new byte[] { (byte) (outCounter >>> 24), + (byte) (outCounter >>> 16), + (byte) (outCounter >>> 8), + (byte) outCounter }); + } + final byte[] C = outMac.doFinal(); + out.write(C); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding C (integrity checksum): " + + Util.dumpString(C)); + } // else ciphertext only; do nothing + } + else + { // no confidentiality; just integrity [+ replay detection] + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding p (plaintext): " + + Util.dumpString(outgoing, offset, len)); + + out.write(outgoing, offset, len); + + // if (outMac != null) { + outMac.update(outgoing, offset, len); + if (replayDetection) + { + outCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "outCounter=" + String.valueOf(outCounter)); + outMac.update(new byte[] { (byte) (outCounter >>> 24), + (byte) (outCounter >>> 16), + (byte) (outCounter >>> 8), + (byte) outCounter }); + } + final byte[] C = outMac.doFinal(); + out.write(C); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding C (integrity checksum): " + + Util.dumpString(C)); + // } // else plaintext only; do nothing + } + + result = out.toByteArray(); + + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("engineWrap()", x); + } + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== engineWrap()"); + return result; + } + + protected String getNegotiatedQOP() + { + if (inMac != null) + { + if (inCipher != null) + { + return Registry.QOP_AUTH_CONF; + } + else + { + return Registry.QOP_AUTH_INT; + } + } + return Registry.QOP_AUTH; + } + + protected String getNegotiatedStrength() + { + if (inMac != null) + { + if (inCipher != null) + { + return Registry.STRENGTH_HIGH; + } + else + { + return Registry.STRENGTH_MEDIUM; + } + } + return Registry.STRENGTH_LOW; + } + + protected String getNegotiatedRawSendSize() + { + return String.valueOf(rawSendSize); + } + + protected String getReuse() + { + return Registry.REUSE_TRUE; + } + + // other methods ----------------------------------------------------------- + + private byte[] sendProtocolElements(final byte[] input) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> sendProtocolElements()"); + if (DEBUG && debuglevel > 6) + debug(TRACE, "C: " + Util.dumpString(input)); + + // Client send U, I, sid, cn + final InputBuffer frameIn = new InputBuffer(input); + try + { + U = frameIn.getText(); // Extract username + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got U (username): \"" + U + "\""); + authorizationID = frameIn.getText(); // Extract authorisation ID + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got I (userid): \"" + authorizationID + "\""); + sid = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sid (session ID): " + new String(sid)); + cn = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got cn (client nonce): " + Util.dumpString(cn)); + cCB = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got cCB (client channel binding): " + + Util.dumpString(cCB)); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendProtocolElements()", x); + } + + // do/can we re-use? + if (ServerStore.instance().isAlive(sid)) + { + final SecurityContext ctx = ServerStore.instance().restoreSession(sid); + srp = SRP.instance(ctx.getMdName()); + K = ctx.getK(); + cIV = ctx.getClientIV(); + sIV = ctx.getServerIV(); + replayDetection = ctx.hasReplayDetection(); + inCounter = ctx.getInCounter(); + outCounter = ctx.getOutCounter(); + inMac = ctx.getInMac(); + outMac = ctx.getOutMac(); + inCipher = ctx.getInCipher(); + outCipher = ctx.getOutCipher(); + + if (sn == null || sn.length != 16) + { + sn = new byte[16]; + } + new SecureRandom ().nextBytes(sn); + + setupSecurityServices(false); + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setScalar(1, 0xFF); + frameOut.setOS(sn); + frameOut.setEOS(channelBinding); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendProtocolElements()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendProtocolElements()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "Old session..."); + if (DEBUG && debuglevel > 2) + debug(INFO, "S: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " sn = " + Util.dumpString(sn)); + if (DEBUG && debuglevel > 2) + debug(INFO, " sCB = " + Util.dumpString(channelBinding)); + return result; + } + else + { // new session + authenticator.activate(properties); + + // ------------------------------------------------------------------- + final HashMap mapB = new HashMap(); + // mapB.put(SRP6KeyAgreement.HASH_FUNCTION, srp.newDigest()); + mapB.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm()); + mapB.put(SRP6KeyAgreement.HOST_PASSWORD_DB, authenticator); + + try + { + serverHandler.init(mapB); + OutgoingMessage out = new OutgoingMessage(); + out.writeString(U); + IncomingMessage in = new IncomingMessage(out.toByteArray()); + out = serverHandler.processMessage(in); + + in = new IncomingMessage(out.toByteArray()); + N = in.readMPI(); + g = in.readMPI(); + s = in.readMPI().toByteArray(); + B = in.readMPI(); + } + catch (KeyAgreementException x) + { + throw new SaslException("sendProtocolElements()", x); + } + // ------------------------------------------------------------------- + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding N (modulus): " + Util.dump(N)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding g (generator): " + Util.dump(g)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding s (client's salt): " + Util.dumpString(s)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding B (server ephemeral public key): " + + Util.dump(B)); + + // The server creates an options list (L), which consists of a + // comma-separated list of option strings that specify the security + // service options the server supports. + L = createL(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding L (available options): \"" + L + "\""); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding sIV (server IV): " + Util.dumpString(sIV)); + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setScalar(1, 0x00); + frameOut.setMPI(N); + frameOut.setMPI(g); + frameOut.setOS(s); + frameOut.setMPI(B); + frameOut.setText(L); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendProtocolElements()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendProtocolElements()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "New session..."); + if (DEBUG && debuglevel > 2) + debug(INFO, "S: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " N = 0x" + N.toString(16)); + if (DEBUG && debuglevel > 2) + debug(INFO, " g = 0x" + g.toString(16)); + if (DEBUG && debuglevel > 2) + debug(INFO, " s = " + Util.dumpString(s)); + if (DEBUG && debuglevel > 2) + debug(INFO, " B = 0x" + B.toString(16)); + if (DEBUG && debuglevel > 2) + debug(INFO, " L = " + L); + return result; + } + } + + private byte[] sendEvidence(final byte[] input) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> sendEvidence()"); + if (DEBUG && debuglevel > 6) + debug(TRACE, "C: " + Util.dumpString(input)); + + // Client send A, M1, o, cIV + final InputBuffer frameIn = new InputBuffer(input); + final byte[] M1; + try + { + A = frameIn.getMPI(); // Extract client's ephemeral public key + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got A (client ephemeral public key): " + Util.dump(A)); + M1 = frameIn.getOS(); // Extract evidence + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got M1 (client evidence): " + Util.dumpString(M1)); + o = frameIn.getText(); // Extract client's options list + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got o (client chosen options): \"" + o + "\""); + cIV = frameIn.getOS(); // Extract client's IV + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got cIV (client IV): " + Util.dumpString(cIV)); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendEvidence()", x); + } + + // Parse client's options and set security layer variables + parseO(o); + + // ---------------------------------------------------------------------- + try + { + final OutgoingMessage out = new OutgoingMessage(); + out.writeMPI(A); + final IncomingMessage in = new IncomingMessage(out.toByteArray()); + serverHandler.processMessage(in); + K = serverHandler.getSharedSecret(); + } + catch (KeyAgreementException x) + { + throw new SaslException("sendEvidence()", x); + } + // ---------------------------------------------------------------------- + + if (DEBUG && debuglevel > 6) + debug(TRACE, "K: " + Util.dumpString(K)); + + final byte[] expected; + try + { + expected = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn, + cCB); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("sendEvidence()", x); + } + + // Verify client evidence + if (!Arrays.equals(M1, expected)) + { + throw new AuthenticationException("M1 mismatch"); + } + + setupSecurityServices(true); + + final byte[] M2; + try + { + M2 = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl, cIV, + sIV, channelBinding); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("sendEvidence()", x); + } + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setOS(M2); + frameOut.setOS(sIV); + frameOut.setEOS(sid); + frameOut.setScalar(4, ttl); + frameOut.setEOS(channelBinding); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendEvidence()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 2) + debug(INFO, "S: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " M2 = " + Util.dumpString(M2)); + if (DEBUG && debuglevel > 2) + debug(INFO, " sIV = " + Util.dumpString(sIV)); + if (DEBUG && debuglevel > 2) + debug(INFO, " sid = " + new String(sid)); + if (DEBUG && debuglevel > 2) + debug(INFO, " ttl = " + ttl); + if (DEBUG && debuglevel > 2) + debug(INFO, " sCB = " + Util.dumpString(channelBinding)); + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendEvidence()"); + return result; + } + + private String createL() + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> createL()"); + + String s = (String) properties.get(SRPRegistry.SRP_MANDATORY); + if (s == null) + { + s = SRPRegistry.DEFAULT_MANDATORY; + } + if (!SRPRegistry.MANDATORY_NONE.equals(s) + && !SRPRegistry.OPTION_REPLAY_DETECTION.equals(s) + && !SRPRegistry.OPTION_INTEGRITY.equals(s) + && !SRPRegistry.OPTION_CONFIDENTIALITY.equals(s)) + { + if (DEBUG && debuglevel > 4) + debug(WARN, "Unrecognised mandatory option (" + s + + "). Using default..."); + s = SRPRegistry.DEFAULT_MANDATORY; + } + + mandatory = s; + + s = (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY); + final boolean confidentiality = (s == null ? SRPRegistry.DEFAULT_CONFIDENTIALITY + : Boolean.valueOf(s).booleanValue()); + + s = (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION); + boolean integrity = (s == null ? SRPRegistry.DEFAULT_INTEGRITY + : Boolean.valueOf(s).booleanValue()); + + s = (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION); + final boolean replayDetection = (s == null ? SRPRegistry.DEFAULT_REPLAY_DETECTION + : Boolean.valueOf(s).booleanValue()); + + final StringBuffer sb = new StringBuffer(); + sb.append(SRPRegistry.OPTION_SRP_DIGEST).append("=").append( + srp.getAlgorithm()).append( + ","); + + if (!SRPRegistry.MANDATORY_NONE.equals(mandatory)) + { + sb.append(SRPRegistry.OPTION_MANDATORY).append("=").append(mandatory).append( + ","); + } + if (replayDetection) + { + sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(","); + // if replay detection is on then force integrity protection + integrity = true; + } + + int i; + if (integrity) + { + for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) + { + sb.append(SRPRegistry.OPTION_INTEGRITY).append("=").append( + SRPRegistry.INTEGRITY_ALGORITHMS[i]).append( + ","); + } + } + + if (confidentiality) + { + IBlockCipher cipher; + for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++) + { + cipher = CipherFactory.getInstance(SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]); + if (cipher != null) + { + sb.append(SRPRegistry.OPTION_CONFIDENTIALITY).append("=").append( + SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]).append( + ","); + } + } + } + + final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE).append( + "=").append( + Registry.SASL_BUFFER_MAX_LIMIT).toString(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== createL()"); + return result; + } + + // Parse client's options and set security layer variables + private void parseO(final String o) throws AuthenticationException + { + this.replayDetection = false; + boolean integrity = false; + boolean confidentiality = false; + String option; + int i; + + final StringTokenizer st = new StringTokenizer(o.toLowerCase(), ","); + while (st.hasMoreTokens()) + { + option = st.nextToken(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "option: <" + option + ">"); + if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION)) + { + replayDetection = true; + } + else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "=")) + { + if (integrity) + { + throw new AuthenticationException( + "Only one integrity algorithm may be chosen"); + } + else + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "algorithm: <" + option + ">"); + for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option)) + { + chosenIntegrityAlgorithm = option; + integrity = true; + break; + } + } + if (!integrity) + { + throw new AuthenticationException( + "Unknown integrity algorithm: " + + option); + } + } + } + else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "=")) + { + if (confidentiality) + { + throw new AuthenticationException( + "Only one confidentiality algorithm may be chosen"); + } + else + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "algorithm: <" + option + ">"); + for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option)) + { + chosenConfidentialityAlgorithm = option; + confidentiality = true; + break; + } + } + if (!confidentiality) + { + throw new AuthenticationException( + "Unknown confidentiality algorithm: " + + option); + } + } + } + else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=")) + { + final String maxBufferSize = option.substring(option.indexOf('=') + 1); + try + { + rawSendSize = Integer.parseInt(maxBufferSize); + if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT + || rawSendSize < 1) + throw new AuthenticationException( + "Illegal value for 'maxbuffersize' option"); + } + catch (NumberFormatException x) + { + throw new AuthenticationException( + SRPRegistry.OPTION_MAX_BUFFER_SIZE + + "=" + + String.valueOf(maxBufferSize), + x); + } + } + } + + // check if client did the right thing + if (replayDetection) + { + if (!integrity) + { + throw new AuthenticationException( + "Missing integrity protection algorithm " + + "but replay detection is chosen"); + } + } + if (mandatory.equals(SRPRegistry.OPTION_REPLAY_DETECTION)) + { + if (!replayDetection) + { + throw new AuthenticationException( + "Replay detection is mandatory but was not chosen"); + } + } + if (mandatory.equals(SRPRegistry.OPTION_INTEGRITY)) + { + if (!integrity) + { + throw new AuthenticationException( + "Integrity protection is mandatory but was not chosen"); + } + } + if (mandatory.equals(SRPRegistry.OPTION_CONFIDENTIALITY)) + { + if (!confidentiality) + { + throw new AuthenticationException( + "Confidentiality is mandatory but was not chosen"); + } + } + + int blockSize = 0; + if (chosenConfidentialityAlgorithm != null) + { + final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm); + if (cipher != null) + { + blockSize = cipher.defaultBlockSize(); + } + else + { // should not happen + throw new AuthenticationException("Confidentiality algorithm (" + + chosenConfidentialityAlgorithm + + ") not available"); + } + } + + sIV = new byte[blockSize]; + if (blockSize > 0) + { + new SecureRandom ().nextBytes(sIV); + } + } + + private void setupSecurityServices(final boolean newSession) + throws SaslException + { + complete = true; // signal end of authentication phase + if (newSession) + { + outCounter = inCounter = 0; + // instantiate cipher if confidentiality protection filter is active + if (chosenConfidentialityAlgorithm != null) + { + if (DEBUG && debuglevel > 2) + debug(INFO, "Activating confidentiality protection filter"); + inCipher = CALG.getInstance(chosenConfidentialityAlgorithm); + outCipher = CALG.getInstance(chosenConfidentialityAlgorithm); + } + // instantiate hmacs if integrity protection filter is active + if (chosenIntegrityAlgorithm != null) + { + if (DEBUG && debuglevel > 2) + debug(INFO, "Activating integrity protection filter"); + inMac = IALG.getInstance(chosenIntegrityAlgorithm); + outMac = IALG.getInstance(chosenIntegrityAlgorithm); + } + + // generate a new sid if at least integrity is used + sid = (inMac != null ? ServerStore.getNewSessionID() : new byte[0]); + } + else + { // same session new keys + K = srp.generateKn(K, cn, sn); + } + + final KDF kdf = KDF.getInstance(K); + + // initialise in/out ciphers if confidentaility protection is used + if (inCipher != null) + { + outCipher.init(kdf, sIV, Direction.FORWARD); + inCipher.init(kdf, cIV, Direction.REVERSED); + } + // initialise in/out macs if integrity protection is used + if (inMac != null) + { + outMac.init(kdf); + inMac.init(kdf); + } + + if (sid != null && sid.length != 0) + { // update the security context and save in map + if (DEBUG && debuglevel > 2) + debug(INFO, "Updating security context for sid = " + new String(sid)); + ServerStore.instance().cacheSession( + ttl, + new SecurityContext( + srp.getAlgorithm(), + sid, + K, + cIV, + sIV, + replayDetection, + inCounter, + outCounter, + inMac, outMac, + inCipher, + outCipher)); + } + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/SecurityContext.java b/gnu/javax/crypto/sasl/srp/SecurityContext.java new file mode 100644 index 000000000..feca25cad --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/SecurityContext.java @@ -0,0 +1,164 @@ +/* SecurityContext.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +/** + * <p>A package-private placeholder for an SRP security context.</p> + */ +class SecurityContext +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private String mdName; + + private byte[] sid; + + private byte[] K; + + private byte[] cIV; + + private byte[] sIV; + + private boolean replayDetection; + + private int inCounter; + + private int outCounter; + + private IALG inMac; + + private IALG outMac; + + private CALG inCipher; + + private CALG outCipher; + + // Constructor(s) + // ------------------------------------------------------------------------- + + SecurityContext(final String mdName, final byte[] sid, final byte[] K, + final byte[] cIV, final byte[] sIV, + final boolean replayDetection, final int inCounter, + final int outCounter, final IALG inMac, final IALG outMac, + final CALG inCipher, final CALG outCipher) + { + super(); + + this.mdName = mdName; + this.sid = sid; + this.K = K; + this.cIV = cIV; + this.sIV = sIV; + this.replayDetection = replayDetection; + this.inCounter = inCounter; + this.outCounter = outCounter; + this.inMac = inMac; + this.outMac = outMac; + this.inCipher = inCipher; + this.outCipher = outCipher; + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + String getMdName() + { + return mdName; + } + + byte[] getSID() + { + return sid; + } + + byte[] getK() + { + return K; + } + + byte[] getClientIV() + { + return cIV; + } + + byte[] getServerIV() + { + return sIV; + } + + boolean hasReplayDetection() + { + return replayDetection; + } + + int getInCounter() + { + return inCounter; + } + + int getOutCounter() + { + return outCounter; + } + + IALG getInMac() + { + return inMac; + } + + IALG getOutMac() + { + return outMac; + } + + CALG getInCipher() + { + return inCipher; + } + + CALG getOutCipher() + { + return outCipher; + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/ServerStore.java b/gnu/javax/crypto/sasl/srp/ServerStore.java new file mode 100644 index 000000000..99bf96a94 --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/ServerStore.java @@ -0,0 +1,196 @@ +/* ServerStore.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +import java.util.HashMap; + +/** + * <p>The server-side implementation of the SRP security context store.</p> + */ +public class ServerStore +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + /** The underlying singleton. */ + private static ServerStore singleton = null; + + /** The map of sid --> Security Context record. */ + private static final HashMap sid2ssc = new HashMap(); + + /** The map of sid --> Session timing record. */ + private static final HashMap sid2ttl = new HashMap(); + + /** A synchronisation lock. */ + private static final Object lock = new Object(); + + /** A counter to generate legible SIDs. */ + private static int counter = 0; + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** Private constructor to enforce Singleton pattern. */ + private ServerStore() + { + super(); + + // TODO: add a cleaning timer thread + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns the classloader Singleton.</p> + * + * @return the classloader Singleton instance. + */ + static synchronized final ServerStore instance() + { + if (singleton == null) + { + singleton = new ServerStore(); + } + return singleton; + } + + /** + * <p>Returns a legible new session identifier.</p> + * + * @return a new session identifier. + */ + static synchronized final byte[] getNewSessionID() + { + final String sid = String.valueOf(++counter); + return new StringBuffer("SID-").append( + "0000000000".substring( + 0, + 10 - sid.length())).append( + sid).toString().getBytes(); + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns a boolean flag indicating if the designated session is still + * alive or not.</p> + * + * @param sid the identifier of the session to check. + * @return <code>true</code> if the designated session is still alive. + * <code>false</code> otherwise. + */ + boolean isAlive(final byte[] sid) + { + boolean result = false; + if (sid != null && sid.length != 0) + { + synchronized (lock) + { + final String key = new String(sid); + final StoreEntry ctx = (StoreEntry) sid2ttl.get(key); + if (ctx != null) + { + result = ctx.isAlive(); + if (!result) + { // invalidate it en-passant + sid2ssc.remove(key); + sid2ttl.remove(key); + } + } + } + } + return result; + } + + /** + * <p>Records a mapping between a session identifier and the Security Context + * of the designated SRP server mechanism instance.</p> + * + * @param ttl the session's Time-To-Live indicator (in seconds). + * @param ctx the server's security context. + */ + void cacheSession(final int ttl, final SecurityContext ctx) + { + synchronized (lock) + { + final String key = new String(ctx.getSID()); + sid2ssc.put(key, ctx); + sid2ttl.put(key, new StoreEntry(ttl)); + } + } + + /** + * <p>Updates the mapping between the designated session identifier and the + * designated server's SASL Security Context. In the process, computes + * and return the underlying mechanism server's evidence that shall be + * returned to the client in a session re-use exchange.</p> + * + * @param sid the identifier of the session to restore. + * @return an SRP server's security context. + */ + SecurityContext restoreSession(final byte[] sid) + { + final String key = new String(sid); + final SecurityContext result; + synchronized (lock) + { + result = (SecurityContext) sid2ssc.remove(key); + sid2ttl.remove(key); + } + return result; + } + + /** + * <p>Removes all information related to the designated session ID.</p> + * + * @param sid the identifier of the seesion to invalidate. + */ + void invalidateSession(final byte[] sid) + { + final String key = new String(sid); + synchronized (lock) + { + sid2ssc.remove(key); + sid2ttl.remove(key); + } + } +}
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/StoreEntry.java b/gnu/javax/crypto/sasl/srp/StoreEntry.java new file mode 100644 index 000000000..c5041fa4b --- /dev/null +++ b/gnu/javax/crypto/sasl/srp/StoreEntry.java @@ -0,0 +1,89 @@ +/* StoreEntry.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.srp; + +/** + * <p>A simple timing-related object for use by SRP re-use code.</p> + */ +class StoreEntry +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private boolean perenial; + + private long timeToDie; + + // Constructor(s) + // ------------------------------------------------------------------------- + + StoreEntry(int ttl) + { + super(); + + if (ttl == 0) + { + perenial = true; + timeToDie = 0L; + } + else + { + perenial = false; + timeToDie = System.currentTimeMillis() + (ttl & 0xFFFFFFFFL) * 1000L; + } + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns <code>true</code> if the Time-To_live period has not elapsed.</p> + * + * @return <code>true</code> if the Time-To-Live period (in seconds) has not + * elapsed yet; <code>false</code> otherwise. + */ + boolean isAlive() + { + return (perenial ? true : (System.currentTimeMillis() < timeToDie)); + } +}
\ No newline at end of file |