summaryrefslogtreecommitdiffstats
path: root/bcprov/src/main/java/org/bouncycastle/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/crypto')
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java16
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java50
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java93
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java333
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java116
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java817
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java71
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java186
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java51
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java253
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java201
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java1494
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java65
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java152
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java181
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java175
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java117
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java237
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java142
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java28
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/io/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java71
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java38
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java279
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java119
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java142
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java88
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java20
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java109
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java30
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java269
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java15
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java487
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java52
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java80
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java96
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java293
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java40
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java43
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java82
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java134
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java41
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java42
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java46
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java61
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java161
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java43
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java77
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java20
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java17
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java70
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java27
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java68
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java15
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java64
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java72
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java105
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java98
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java133
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java92
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java32
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java380
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java71
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java78
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java186
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java85
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java138
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java77
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java29
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java157
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java72
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java20
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java56
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java15
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java15
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java17
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java131
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java176
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java127
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java34
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java43
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java63
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java112
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java86
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java142
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java28
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java143
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java29
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java574
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java76
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java36
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java47
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java62
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java254
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java83
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java77
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java240
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java69
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java133
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java12
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java582
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java70
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java65
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java70
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java68
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java49
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java29
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java43
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java392
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java12
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java48
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java69
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java413
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java104
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java22
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java31
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java25
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/package.html5
210 files changed, 12677 insertions, 2860 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
index bdb694d..dd056ac 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
@@ -54,7 +54,7 @@ public class BufferedBlockCipher
}
else
{
- partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx)));
+ partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("GCFB", idx) ||name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx)));
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
index ef6e29e..0e2b4b0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
@@ -7,11 +7,6 @@ public interface DerivationFunction
{
public void init(DerivationParameters param);
- /**
- * return the message digest used as the basis for the function
- */
- public Digest getDigest();
-
public int generateBytes(byte[] out, int outOff, int len)
throws DataLengthException, IllegalArgumentException;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java
new file mode 100644
index 0000000..180382d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto;
+
+/**
+ * base interface for general purpose Digest based byte derivation functions.
+ */
+public interface DigestDerivationFunction
+ extends DerivationFunction
+{
+ /**
+ * return the message digest used as the basis for the function
+ */
+ public Digest getDigest();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java
new file mode 100644
index 0000000..16198ba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto;
+
+/**
+ * base interface for general purpose Mac based byte derivation functions.
+ */
+public interface MacDerivationFunction
+ extends DerivationFunction
+{
+ /**
+ * return the MAC used as the basis for the function
+ */
+ public Mac getMac();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
index 59944e0..a491b9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
@@ -42,10 +42,13 @@ public class ECDHBasicAgreement
CipherParameters pubKey)
{
ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
- ECPoint P = pub.getQ().multiply(key.getD());
+ ECPoint P = pub.getQ().multiply(key.getD()).normalize();
- // if (p.isInfinity()) throw new RuntimeException("d*Q == infinity");
+ if (P.isInfinity())
+ {
+ throw new IllegalStateException("Infinity is not a valid agreement value for ECDH");
+ }
- return P.getX().toBigInteger();
+ return P.getAffineXCoord().toBigInteger();
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
index 12b8405..28140df 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
@@ -47,12 +47,18 @@ public class ECDHCBasicAgreement
public BigInteger calculateAgreement(
CipherParameters pubKey)
{
- ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
- ECDomainParameters params = pub.getParameters();
- ECPoint P = pub.getQ().multiply(params.getH().multiply(key.getD()));
+ ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
+ ECDomainParameters params = pub.getParameters();
- // if (p.isInfinity()) throw new RuntimeException("Invalid public key");
+ BigInteger hd = params.getH().multiply(key.getD()).mod(params.getN());
- return P.getX().toBigInteger();
+ ECPoint P = pub.getQ().multiply(hd).normalize();
+
+ if (P.isInfinity())
+ {
+ throw new IllegalStateException("Infinity is not a valid agreement value for ECDHC");
+ }
+
+ return P.getAffineXCoord().toBigInteger();
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
index da88b4a..794f555 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
@@ -11,6 +11,7 @@ import org.bouncycastle.crypto.params.MQVPrivateParameters;
import org.bouncycastle.crypto.params.MQVPublicParameters;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
public class ECMQVBasicAgreement
@@ -37,9 +38,14 @@ public class ECMQVBasicAgreement
ECPoint agreement = calculateMqvAgreement(staticPrivateKey.getParameters(), staticPrivateKey,
privParams.getEphemeralPrivateKey(), privParams.getEphemeralPublicKey(),
- pubParams.getStaticPublicKey(), pubParams.getEphemeralPublicKey());
+ pubParams.getStaticPublicKey(), pubParams.getEphemeralPublicKey()).normalize();
- return agreement.getX().toBigInteger();
+ if (agreement.isInfinity())
+ {
+ throw new IllegalStateException("Infinity is not a valid agreement value for MQV");
+ }
+
+ return agreement.getAffineXCoord().toBigInteger();
}
// The ECMQV Primitive as described in SEC-1, 3.4
@@ -55,37 +61,31 @@ public class ECMQVBasicAgreement
int e = (n.bitLength() + 1) / 2;
BigInteger powE = ECConstants.ONE.shiftLeft(e);
- // The Q2U public key is optional
- ECPoint q;
- if (Q2U == null)
- {
- q = parameters.getG().multiply(d2U.getD());
- }
- else
- {
- q = Q2U.getQ();
- }
+ ECCurve curve = parameters.getCurve();
- BigInteger x = q.getX().toBigInteger();
+ ECPoint[] points = new ECPoint[]{
+ // The Q2U public key is optional
+ ECAlgorithms.importPoint(curve, Q2U == null ? parameters.getG().multiply(d2U.getD()) : Q2U.getQ()),
+ ECAlgorithms.importPoint(curve, Q1V.getQ()),
+ ECAlgorithms.importPoint(curve, Q2V.getQ())
+ };
+
+ curve.normalizeAll(points);
+
+ ECPoint q2u = points[0], q1v = points[1], q2v = points[2];
+
+ BigInteger x = q2u.getAffineXCoord().toBigInteger();
BigInteger xBar = x.mod(powE);
BigInteger Q2UBar = xBar.setBit(e);
- BigInteger s = d1U.getD().multiply(Q2UBar).mod(n).add(d2U.getD()).mod(n);
+ BigInteger s = d1U.getD().multiply(Q2UBar).add(d2U.getD()).mod(n);
- BigInteger xPrime = Q2V.getQ().getX().toBigInteger();
+ BigInteger xPrime = q2v.getAffineXCoord().toBigInteger();
BigInteger xPrimeBar = xPrime.mod(powE);
BigInteger Q2VBar = xPrimeBar.setBit(e);
BigInteger hs = parameters.getH().multiply(s).mod(n);
-// ECPoint p = Q1V.getQ().multiply(Q2VBar).add(Q2V.getQ()).multiply(hs);
- ECPoint p = ECAlgorithms.sumOfTwoMultiplies(
- Q1V.getQ(), Q2VBar.multiply(hs).mod(n), Q2V.getQ(), hs);
-
- if (p.isInfinity())
- {
- throw new IllegalStateException("Infinity is not a valid agreement value for MQV");
- }
-
- return p;
+ return ECAlgorithms.sumOfTwoMultiplies(
+ q1v, Q2VBar.multiply(hs).mod(n), q2v, hs);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html
deleted file mode 100644
index db47144..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Password Authenticated Key Exchange by Juggling (J-PAKE).
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
index 6803953..500b1dd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
@@ -11,9 +11,9 @@ import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
import org.bouncycastle.crypto.DerivationParameters;
import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.DigestDerivationFunction;
import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
import org.bouncycastle.crypto.params.KDFParameters;
import org.bouncycastle.crypto.util.Pack;
@@ -22,9 +22,9 @@ import org.bouncycastle.crypto.util.Pack;
* X9.63 based key derivation function for ECDH CMS.
*/
public class ECDHKEKGenerator
- implements DerivationFunction
+ implements DigestDerivationFunction
{
- private DerivationFunction kdf;
+ private DigestDerivationFunction kdf;
private ASN1ObjectIdentifier algorithm;
private int keySize;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html
deleted file mode 100644
index 4b49331..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Basic key agreement classes.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java
new file mode 100644
index 0000000..3969fe8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.crypto.commitments;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Commitment;
+import org.bouncycastle.crypto.Committer;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A basic hash-committer based on the one described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking",
+ * by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002).
+ * <p>
+ * The algorithm used by this class differs from the one given in that it includes the length of the message in the hash calculation.
+ * </p>
+ */
+public class GeneralHashCommitter
+ implements Committer
+{
+ private final Digest digest;
+ private final int byteLength;
+ private final SecureRandom random;
+
+ /**
+ * Base Constructor. The maximum message length that can be committed to is half the length of the internal
+ * block size for the digest (ExtendedDigest.getBlockLength()).
+ *
+ * @param digest digest to use for creating commitments.
+ * @param random source of randomness for generating secrets.
+ */
+ public GeneralHashCommitter(ExtendedDigest digest, SecureRandom random)
+ {
+ this.digest = digest;
+ this.byteLength = digest.getByteLength();
+ this.random = random;
+ }
+
+ /**
+ * Generate a commitment for the passed in message.
+ *
+ * @param message the message to be committed to,
+ * @return a Commitment
+ */
+ public Commitment commit(byte[] message)
+ {
+ if (message.length > byteLength / 2)
+ {
+ throw new DataLengthException("Message to be committed to too large for digest.");
+ }
+
+ byte[] w = new byte[byteLength - message.length];
+
+ random.nextBytes(w);
+
+ return new Commitment(w, calculateCommitment(w, message));
+ }
+
+ /**
+ * Return true if the passed in commitment represents a commitment to the passed in message.
+ *
+ * @param commitment a commitment previously generated.
+ * @param message the message that was expected to have been committed to.
+ * @return true if commitment matches message, false otherwise.
+ */
+ public boolean isRevealed(Commitment commitment, byte[] message)
+ {
+ if (message.length + commitment.getSecret().length != byteLength)
+ {
+ throw new DataLengthException("Message and witness secret lengths do not match.");
+ }
+
+ byte[] calcCommitment = calculateCommitment(commitment.getSecret(), message);
+
+ return Arrays.constantTimeAreEqual(commitment.getCommitment(), calcCommitment);
+ }
+
+ private byte[] calculateCommitment(byte[] w, byte[] message)
+ {
+ byte[] commitment = new byte[digest.getDigestSize()];
+
+ digest.update(w, 0, w.length);
+ digest.update(message, 0, message.length);
+
+ digest.update((byte)((message.length >>> 8)));
+ digest.update((byte)(message.length));
+
+ digest.doFinal(commitment, 0);
+
+ return commitment;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java
index 1494c3c..b5860b5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java
@@ -12,6 +12,9 @@ import org.bouncycastle.util.Arrays;
/**
* A basic hash-committer as described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking",
* by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002).
+ * <p>
+ * Use this class if you can enforce fixed length for messages. If you need something more general, use the GeneralHashCommitter.
+ * </p>
*/
public class HashCommitter
implements Committer
@@ -55,7 +58,7 @@ public class HashCommitter
}
/**
- * Return true if the passed in commitment represents a commitment to the passed in maessage.
+ * Return true if the passed in commitment represents a commitment to the passed in message.
*
* @param commitment a commitment previously generated.
* @param message the message that was expected to have been committed to.
@@ -63,6 +66,11 @@ public class HashCommitter
*/
public boolean isRevealed(Commitment commitment, byte[] message)
{
+ if (message.length + commitment.getSecret().length != byteLength)
+ {
+ throw new DataLengthException("Message and witness secret lengths do not match.");
+ }
+
byte[] calcCommitment = calculateCommitment(commitment.getSecret(), message);
return Arrays.constantTimeAreEqual(commitment.getCommitment(), calcCommitment);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html
deleted file mode 100644
index 302cc60..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Commitment algorithms.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
new file mode 100644
index 0000000..55e579e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
@@ -0,0 +1,333 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Implementation of Chinese SM3 digest as described at
+ * http://tools.ietf.org/html/draft-shen-sm3-hash-00
+ * and at .... ( Chinese PDF )
+ * <p/>
+ * The specification says "process a bit stream",
+ * but this is written to process bytes in blocks of 4,
+ * meaning this will process 32-bit word groups.
+ * But so do also most other digest specifications,
+ * including the SHA-256 which was a origin for
+ * this specification.
+ */
+public class SM3Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 32; // bytes
+ private static final int BLOCK_SIZE = 64 / 4; // of 32 bit ints (16 ints)
+
+ private int[] V = new int[DIGEST_LENGTH / 4]; // in 32 bit ints (8 ints)
+ private int[] inwords = new int[BLOCK_SIZE];
+ private int xOff;
+
+ // Work-bufs used within processBlock()
+ private int[] W = new int[68];
+ private int[] W1 = new int[64];
+
+ // Round constant T for processBlock() which is 32 bit integer rolled left up to (63 MOD 32) bit positions.
+ private static final int[] T = new int[64];
+
+ static
+ {
+ for (int i = 0; i < 16; ++i)
+ {
+ int t = 0x79CC4519;
+ T[i] = (t << i) | (t >>> (32 - i));
+ }
+ for (int i = 16; i < 64; ++i)
+ {
+ int n = i % 32;
+ int t = 0x7A879D8A;
+ T[i] = (t << n) | (t >>> (32 - n));
+ }
+ }
+
+
+ /**
+ * Standard constructor
+ */
+ public SM3Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public SM3Digest(SM3Digest t)
+ {
+ super(t);
+
+ copyIn(t);
+ }
+
+ private void copyIn(SM3Digest t)
+ {
+ System.arraycopy(t.V, 0, this.V, 0, this.V.length);
+ System.arraycopy(t.inwords, 0, this.inwords, 0, this.inwords.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SM3";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+
+ public Memoable copy()
+ {
+ return new SM3Digest(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ SM3Digest d = (SM3Digest)other;
+
+ super.copyIn(d);
+ copyIn(d);
+ }
+
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ this.V[0] = 0x7380166F;
+ this.V[1] = 0x4914B2B9;
+ this.V[2] = 0x172442D7;
+ this.V[3] = 0xDA8A0600;
+ this.V[4] = 0xA96F30BC;
+ this.V[5] = 0x163138AA;
+ this.V[6] = 0xE38DEE4D;
+ this.V[7] = 0xB0FB0E4E;
+
+ this.xOff = 0;
+ }
+
+
+ public int doFinal(byte[] out,
+ int outOff)
+ {
+ finish();
+
+ Pack.intToBigEndian(this.V[0], out, outOff + 0);
+ Pack.intToBigEndian(this.V[1], out, outOff + 4);
+ Pack.intToBigEndian(this.V[2], out, outOff + 8);
+ Pack.intToBigEndian(this.V[3], out, outOff + 12);
+ Pack.intToBigEndian(this.V[4], out, outOff + 16);
+ Pack.intToBigEndian(this.V[5], out, outOff + 20);
+ Pack.intToBigEndian(this.V[6], out, outOff + 24);
+ Pack.intToBigEndian(this.V[7], out, outOff + 28);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+
+ protected void processWord(byte[] in,
+ int inOff)
+ {
+ // Note: Inlined for performance
+ // this.inwords[xOff] = Pack.bigEndianToInt(in, inOff);
+ int n = (((in[inOff] & 0xff) << 24) |
+ ((in[++inOff] & 0xff) << 16) |
+ ((in[++inOff] & 0xff) << 8) |
+ ((in[++inOff] & 0xff)));
+
+ this.inwords[this.xOff] = n;
+ ++this.xOff;
+
+ if (this.xOff >= 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(long bitLength)
+ {
+ if (this.xOff > (BLOCK_SIZE - 2))
+ {
+ // xOff == 15 --> can't fit the 64 bit length field at tail..
+ this.inwords[this.xOff] = 0; // fill with zero
+ ++this.xOff;
+
+ processBlock();
+ }
+ // Fill with zero words, until reach 2nd to last slot
+ while (this.xOff < (BLOCK_SIZE - 2))
+ {
+ this.inwords[this.xOff] = 0;
+ ++this.xOff;
+ }
+
+ // Store input data length in BITS
+ this.inwords[this.xOff++] = (int)(bitLength >>> 32);
+ this.inwords[this.xOff++] = (int)(bitLength);
+ }
+
+/*
+
+3.4.2. Constants
+
+
+ Tj = 79cc4519 when 0 < = j < = 15
+ Tj = 7a879d8a when 16 < = j < = 63
+
+3.4.3. Boolean function
+
+
+ FFj(X;Y;Z) = X XOR Y XOR Z when 0 < = j < = 15
+ = (X AND Y) OR (X AND Z) OR (Y AND Z) when 16 < = j < = 63
+
+ GGj(X;Y;Z) = X XOR Y XOR Z when 0 < = j < = 15
+ = (X AND Y) OR (NOT X AND Z) when 16 < = j < = 63
+
+ The X, Y, Z in the fomular are words!GBP
+
+3.4.4. Permutation function
+
+
+ P0(X) = X XOR (X <<< 9) XOR (X <<< 17) ## ROLL, not SHIFT
+ P1(X) = X XOR (X <<< 15) XOR (X <<< 23) ## ROLL, not SHIFT
+
+ The X in the fomular are a word.
+
+----------
+
+Each ROLL converted to Java expression:
+
+ROLL 9 : ((x << 9) | (x >>> (32-9))))
+ROLL 17 : ((x << 17) | (x >>> (32-17)))
+ROLL 15 : ((x << 15) | (x >>> (32-15)))
+ROLL 23 : ((x << 23) | (x >>> (32-23)))
+
+ */
+
+ private int P0(final int x)
+ {
+ final int r9 = ((x << 9) | (x >>> (32 - 9)));
+ final int r17 = ((x << 17) | (x >>> (32 - 17)));
+ return (x ^ r9 ^ r17);
+ }
+
+ private int P1(final int x)
+ {
+ final int r15 = ((x << 15) | (x >>> (32 - 15)));
+ final int r23 = ((x << 23) | (x >>> (32 - 23)));
+ return (x ^ r15 ^ r23);
+ }
+
+ private int FF0(final int x, final int y, final int z)
+ {
+ return (x ^ y ^ z);
+ }
+
+ private int FF1(final int x, final int y, final int z)
+ {
+ return ((x & y) | (x & z) | (y & z));
+ }
+
+ private int GG0(final int x, final int y, final int z)
+ {
+ return (x ^ y ^ z);
+ }
+
+ private int GG1(final int x, final int y, final int z)
+ {
+ return ((x & y) | ((~x) & z));
+ }
+
+
+ protected void processBlock()
+ {
+ for (int j = 0; j < 16; ++j)
+ {
+ this.W[j] = this.inwords[j];
+ }
+ for (int j = 16; j < 68; ++j)
+ {
+ int wj3 = this.W[j - 3];
+ int r15 = ((wj3 << 15) | (wj3 >>> (32 - 15)));
+ int wj13 = this.W[j - 13];
+ int r7 = ((wj13 << 7) | (wj13 >>> (32 - 7)));
+ this.W[j] = P1(this.W[j - 16] ^ this.W[j - 9] ^ r15) ^ r7 ^ this.W[j - 6];
+ }
+ for (int j = 0; j < 64; ++j)
+ {
+ this.W1[j] = this.W[j] ^ this.W[j + 4];
+ }
+
+ int A = this.V[0];
+ int B = this.V[1];
+ int C = this.V[2];
+ int D = this.V[3];
+ int E = this.V[4];
+ int F = this.V[5];
+ int G = this.V[6];
+ int H = this.V[7];
+
+
+ for (int j = 0; j < 16; ++j)
+ {
+ int a12 = ((A << 12) | (A >>> (32 - 12)));
+ int s1_ = a12 + E + T[j];
+ int SS1 = ((s1_ << 7) | (s1_ >>> (32 - 7)));
+ int SS2 = SS1 ^ a12;
+ int TT1 = FF0(A, B, C) + D + SS2 + this.W1[j];
+ int TT2 = GG0(E, F, G) + H + SS1 + this.W[j];
+ D = C;
+ C = ((B << 9) | (B >>> (32 - 9)));
+ B = A;
+ A = TT1;
+ H = G;
+ G = ((F << 19) | (F >>> (32 - 19)));
+ F = E;
+ E = P0(TT2);
+ }
+
+ // Different FF,GG functions on rounds 16..63
+ for (int j = 16; j < 64; ++j)
+ {
+ int a12 = ((A << 12) | (A >>> (32 - 12)));
+ int s1_ = a12 + E + T[j];
+ int SS1 = ((s1_ << 7) | (s1_ >>> (32 - 7)));
+ int SS2 = SS1 ^ a12;
+ int TT1 = FF1(A, B, C) + D + SS2 + this.W1[j];
+ int TT2 = GG1(E, F, G) + H + SS1 + this.W[j];
+ D = C;
+ C = ((B << 9) | (B >>> (32 - 9)));
+ B = A;
+ A = TT1;
+ H = G;
+ G = ((F << 19) | (F >>> (32 - 19)));
+ F = E;
+ E = P0(TT2);
+ }
+
+ this.V[0] ^= A;
+ this.V[1] ^= B;
+ this.V[2] ^= C;
+ this.V[3] ^= D;
+ this.V[4] ^= E;
+ this.V[5] ^= F;
+ this.V[6] ^= G;
+ this.V[7] ^= H;
+
+ this.xOff = 0;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
new file mode 100644
index 0000000..06eaabd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.SkeinParameters;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes,
+ * based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p/>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p/>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p/>
+ *
+ * @see SkeinEngine
+ * @see SkeinParameters
+ */
+public class SkeinDigest
+ implements ExtendedDigest, Memoable
+{
+ /**
+ * 256 bit block size - Skein-256
+ */
+ public static final int SKEIN_256 = SkeinEngine.SKEIN_256;
+ /**
+ * 512 bit block size - Skein-512
+ */
+ public static final int SKEIN_512 = SkeinEngine.SKEIN_512;
+ /**
+ * 1024 bit block size - Skein-1024
+ */
+ public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024;
+
+ private SkeinEngine engine;
+
+ /**
+ * Constructs a Skein digest with an internal state size and output size.
+ *
+ * @param stateSizeBits the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+ * {@link #SKEIN_1024}.
+ * @param digestSizeBits the output/digest size to produce in bits, which must be an integral number of
+ * bytes.
+ */
+ public SkeinDigest(int stateSizeBits, int digestSizeBits)
+ {
+ this.engine = new SkeinEngine(stateSizeBits, digestSizeBits);
+ init(null);
+ }
+
+ public SkeinDigest(SkeinDigest digest)
+ {
+ this.engine = new SkeinEngine(digest.engine);
+ }
+
+ public void reset(Memoable other)
+ {
+ SkeinDigest d = (SkeinDigest)other;
+ engine.reset(d.engine);
+ }
+
+ public Memoable copy()
+ {
+ return new SkeinDigest(this);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Skein-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8);
+ }
+
+ public int getDigestSize()
+ {
+ return engine.getOutputSize();
+ }
+
+ public int getByteLength()
+ {
+ return engine.getBlockSize();
+ }
+
+ /**
+ * Optionally initialises the Skein digest with the provided parameters.<br>
+ * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function.
+ *
+ * @param params the parameters to apply to this engine, or <code>null</code> to use no parameters.
+ */
+ public void init(SkeinParameters params)
+ {
+ engine.init(params);
+ }
+
+ public void reset()
+ {
+ engine.reset();
+ }
+
+ public void update(byte in)
+ {
+ engine.update(in);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ engine.update(in, inOff, len);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ return engine.doFinal(out, outOff);
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
new file mode 100644
index 0000000..bca524e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
@@ -0,0 +1,817 @@
+package org.bouncycastle.crypto.digests;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.crypto.params.SkeinParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block
+ * sizes, based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p/>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p/>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p/>
+ * This implementation is the basis for {@link SkeinDigest} and {@link SkeinMac}, implementing the
+ * parameter based configuration system that allows Skein to be adapted to multiple applications. <br>
+ * Initialising the engine with {@link SkeinParameters} allows standard and arbitrary parameters to
+ * be applied during the Skein hash function.
+ * <p/>
+ * Implemented:
+ * <ul>
+ * <li>256, 512 and 1024 bit internal states.</li>
+ * <li>Full 96 bit input length.</li>
+ * <li>Parameters defined in the Skein specification, and arbitrary other pre and post message
+ * parameters.</li>
+ * <li>Arbitrary output size in 1 byte intervals.</li>
+ * </ul>
+ * <p/>
+ * Not implemented:
+ * <ul>
+ * <li>Sub-byte length input (bit padding).</li>
+ * <li>Tree hashing.</li>
+ * </ul>
+ *
+ * @see SkeinParameters
+ */
+public class SkeinEngine
+ implements Memoable
+{
+ /**
+ * 256 bit block size - Skein 256
+ */
+ public static final int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256;
+ /**
+ * 512 bit block size - Skein 512
+ */
+ public static final int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512;
+ /**
+ * 1024 bit block size - Skein 1024
+ */
+ public static final int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024;
+
+ // Minimal at present, but more complex when tree hashing is implemented
+ private static class Configuration
+ {
+ private byte[] bytes = new byte[32];
+
+ public Configuration(long outputSizeBits)
+ {
+ // 0..3 = ASCII SHA3
+ bytes[0] = (byte)'S';
+ bytes[1] = (byte)'H';
+ bytes[2] = (byte)'A';
+ bytes[3] = (byte)'3';
+
+ // 4..5 = version number in LSB order
+ bytes[4] = 1;
+ bytes[5] = 0;
+
+ // 8..15 = output length
+ ThreefishEngine.wordToBytes(outputSizeBits, bytes, 8);
+ }
+
+ public byte[] getBytes()
+ {
+ return bytes;
+ }
+
+ }
+
+ public static class Parameter
+ {
+ private int type;
+ private byte[] value;
+
+ public Parameter(int type, byte[] value)
+ {
+ this.type = type;
+ this.value = value;
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public byte[] getValue()
+ {
+ return value;
+ }
+
+ }
+
+ /**
+ * The parameter type for the Skein key.
+ */
+ private static final int PARAM_TYPE_KEY = 0;
+
+ /**
+ * The parameter type for the Skein configuration block.
+ */
+ private static final int PARAM_TYPE_CONFIG = 4;
+
+ /**
+ * The parameter type for the message.
+ */
+ private static final int PARAM_TYPE_MESSAGE = 48;
+
+ /**
+ * The parameter type for the output transformation.
+ */
+ private static final int PARAM_TYPE_OUTPUT = 63;
+
+ /**
+ * Precalculated UBI(CFG) states for common state/output combinations without key or other
+ * pre-message params.
+ */
+ private static final Hashtable INITIAL_STATES = new Hashtable();
+
+ static
+ {
+ // From Appendix C of the Skein 1.3 NIST submission
+ initialState(SKEIN_256, 128, new long[]{
+ 0xe1111906964d7260L,
+ 0x883daaa77c8d811cL,
+ 0x10080df491960f7aL,
+ 0xccf7dde5b45bc1c2L});
+
+ initialState(SKEIN_256, 160, new long[]{
+ 0x1420231472825e98L,
+ 0x2ac4e9a25a77e590L,
+ 0xd47a58568838d63eL,
+ 0x2dd2e4968586ab7dL});
+
+ initialState(SKEIN_256, 224, new long[]{
+ 0xc6098a8c9ae5ea0bL,
+ 0x876d568608c5191cL,
+ 0x99cb88d7d7f53884L,
+ 0x384bddb1aeddb5deL});
+
+ initialState(SKEIN_256, 256, new long[]{
+ 0xfc9da860d048b449L,
+ 0x2fca66479fa7d833L,
+ 0xb33bc3896656840fL,
+ 0x6a54e920fde8da69L});
+
+ initialState(SKEIN_512, 128, new long[]{
+ 0xa8bc7bf36fbf9f52L,
+ 0x1e9872cebd1af0aaL,
+ 0x309b1790b32190d3L,
+ 0xbcfbb8543f94805cL,
+ 0x0da61bcd6e31b11bL,
+ 0x1a18ebead46a32e3L,
+ 0xa2cc5b18ce84aa82L,
+ 0x6982ab289d46982dL});
+
+ initialState(SKEIN_512, 160, new long[]{
+ 0x28b81a2ae013bd91L,
+ 0xc2f11668b5bdf78fL,
+ 0x1760d8f3f6a56f12L,
+ 0x4fb747588239904fL,
+ 0x21ede07f7eaf5056L,
+ 0xd908922e63ed70b8L,
+ 0xb8ec76ffeccb52faL,
+ 0x01a47bb8a3f27a6eL});
+
+ initialState(SKEIN_512, 224, new long[]{
+ 0xccd0616248677224L,
+ 0xcba65cf3a92339efL,
+ 0x8ccd69d652ff4b64L,
+ 0x398aed7b3ab890b4L,
+ 0x0f59d1b1457d2bd0L,
+ 0x6776fe6575d4eb3dL,
+ 0x99fbc70e997413e9L,
+ 0x9e2cfccfe1c41ef7L});
+
+ initialState(SKEIN_512, 384, new long[]{
+ 0xa3f6c6bf3a75ef5fL,
+ 0xb0fef9ccfd84faa4L,
+ 0x9d77dd663d770cfeL,
+ 0xd798cbf3b468fddaL,
+ 0x1bc4a6668a0e4465L,
+ 0x7ed7d434e5807407L,
+ 0x548fc1acd4ec44d6L,
+ 0x266e17546aa18ff8L});
+
+ initialState(SKEIN_512, 512, new long[]{
+ 0x4903adff749c51ceL,
+ 0x0d95de399746df03L,
+ 0x8fd1934127c79bceL,
+ 0x9a255629ff352cb1L,
+ 0x5db62599df6ca7b0L,
+ 0xeabe394ca9d5c3f4L,
+ 0x991112c71a75b523L,
+ 0xae18a40b660fcc33L});
+ }
+
+ private static void initialState(int blockSize, int outputSize, long[] state)
+ {
+ INITIAL_STATES.put(variantIdentifier(blockSize / 8, outputSize / 8), state);
+ }
+
+ private static Integer variantIdentifier(int blockSizeBytes, int outputSizeBytes)
+ {
+ return new Integer((outputSizeBytes << 16) | blockSizeBytes);
+ }
+
+ private static class UbiTweak
+ {
+ /**
+ * Point at which position might overflow long, so switch to add with carry logic
+ */
+ private static final long LOW_RANGE = Long.MAX_VALUE - Integer.MAX_VALUE;
+
+ /**
+ * Bit 127 = final
+ */
+ private static final long T1_FINAL = 1L << 63;
+
+ /**
+ * Bit 126 = first
+ */
+ private static final long T1_FIRST = 1L << 62;
+
+ /**
+ * UBI uses a 128 bit tweak
+ */
+ private long tweak[] = new long[2];
+
+ /**
+ * Whether 64 bit position exceeded
+ */
+ private boolean extendedPosition;
+
+ public UbiTweak()
+ {
+ reset();
+ }
+
+ public void reset(UbiTweak tweak)
+ {
+ this.tweak = Arrays.clone(tweak.tweak, this.tweak);
+ this.extendedPosition = tweak.extendedPosition;
+ }
+
+ public void reset()
+ {
+ tweak[0] = 0;
+ tweak[1] = 0;
+ extendedPosition = false;
+ setFirst(true);
+ }
+
+ public void setType(int type)
+ {
+ // Bits 120..125 = type
+ tweak[1] = (tweak[1] & 0xFFFFFFC000000000L) | ((type & 0x3FL) << 56);
+ }
+
+ public int getType()
+ {
+ return (int)((tweak[1] >>> 56) & 0x3FL);
+ }
+
+ public void setFirst(boolean first)
+ {
+ if (first)
+ {
+ tweak[1] |= T1_FIRST;
+ }
+ else
+ {
+ tweak[1] &= ~T1_FIRST;
+ }
+ }
+
+ public boolean isFirst()
+ {
+ return ((tweak[1] & T1_FIRST) != 0);
+ }
+
+ public void setFinal(boolean last)
+ {
+ if (last)
+ {
+ tweak[1] |= T1_FINAL;
+ }
+ else
+ {
+ tweak[1] &= ~T1_FINAL;
+ }
+ }
+
+ public boolean isFinal()
+ {
+ return ((tweak[1] & T1_FINAL) != 0);
+ }
+
+ /**
+ * Advances the position in the tweak by the specified value.
+ */
+ public void advancePosition(int advance)
+ {
+ // Bits 0..95 = position
+ if (extendedPosition)
+ {
+ long[] parts = new long[3];
+ parts[0] = tweak[0] & 0xFFFFFFFFL;
+ parts[1] = (tweak[0] >>> 32) & 0xFFFFFFFFL;
+ parts[2] = tweak[1] & 0xFFFFFFFFL;
+
+ long carry = advance;
+ for (int i = 0; i < parts.length; i++)
+ {
+ carry += parts[i];
+ parts[i] = carry;
+ carry >>>= 32;
+ }
+ tweak[0] = ((parts[1] & 0xFFFFFFFFL) << 32) | (parts[0] & 0xFFFFFFFFL);
+ tweak[1] = (tweak[1] & 0xFFFFFFFF00000000L) | (parts[2] & 0xFFFFFFFFL);
+ }
+ else
+ {
+ long position = tweak[0];
+ position += advance;
+ tweak[0] = position;
+ if (position > LOW_RANGE)
+ {
+ extendedPosition = true;
+ }
+ }
+ }
+
+ public long[] getWords()
+ {
+ return tweak;
+ }
+
+ public String toString()
+ {
+ return getType() + " first: " + isFirst() + ", final: " + isFinal();
+ }
+
+ }
+
+ /**
+ * The Unique Block Iteration chaining mode.
+ */
+ // TODO: This might be better as methods...
+ private class UBI
+ {
+ private final UbiTweak tweak = new UbiTweak();
+
+ /**
+ * Buffer for the current block of message data
+ */
+ private byte[] currentBlock;
+
+ /**
+ * Offset into the current message block
+ */
+ private int currentOffset;
+
+ /**
+ * Buffer for message words for feedback into encrypted block
+ */
+ private long[] message;
+
+ public UBI(int blockSize)
+ {
+ currentBlock = new byte[blockSize];
+ message = new long[currentBlock.length / 8];
+ }
+
+ public void reset(UBI ubi)
+ {
+ currentBlock = Arrays.clone(ubi.currentBlock, currentBlock);
+ currentOffset = ubi.currentOffset;
+ message = Arrays.clone(ubi.message, this.message);
+ tweak.reset(ubi.tweak);
+ }
+
+ public void reset(int type)
+ {
+ tweak.reset();
+ tweak.setType(type);
+ currentOffset = 0;
+ }
+
+ public void update(byte[] value, int offset, int len, long[] output)
+ {
+ /*
+ * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
+ * are subsequent bytes (last block must be processed in doFinal() with final=true set).
+ */
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentOffset == currentBlock.length)
+ {
+ processBlock(output);
+ tweak.setFirst(false);
+ currentOffset = 0;
+ }
+
+ int toCopy = Math.min((len - copied), currentBlock.length - currentOffset);
+ System.arraycopy(value, offset + copied, currentBlock, currentOffset, toCopy);
+ copied += toCopy;
+ currentOffset += toCopy;
+ tweak.advancePosition(toCopy);
+ }
+ }
+
+ private void processBlock(long[] output)
+ {
+ threefish.init(true, chain, tweak.getWords());
+ for (int i = 0; i < message.length; i++)
+ {
+ message[i] = ThreefishEngine.bytesToWord(currentBlock, i * 8);
+ }
+
+ threefish.processBlock(message, output);
+
+ for (int i = 0; i < output.length; i++)
+ {
+ output[i] ^= message[i];
+ }
+ }
+
+ public void doFinal(long[] output)
+ {
+ // Pad remainder of current block with zeroes
+ for (int i = currentOffset; i < currentBlock.length; i++)
+ {
+ currentBlock[i] = 0;
+ }
+
+ tweak.setFinal(true);
+ processBlock(output);
+ }
+
+ }
+
+ /**
+ * Underlying Threefish tweakable block cipher
+ */
+ final ThreefishEngine threefish;
+
+ /**
+ * Size of the digest output, in bytes
+ */
+ private final int outputSizeBytes;
+
+ /**
+ * The current chaining/state value
+ */
+ long[] chain;
+
+ /**
+ * The initial state value
+ */
+ private long[] initialState;
+
+ /**
+ * The (optional) key parameter
+ */
+ private byte[] key;
+
+ /**
+ * Parameters to apply prior to the message
+ */
+ private Parameter[] preMessageParameters;
+
+ /**
+ * Parameters to apply after the message, but prior to output
+ */
+ private Parameter[] postMessageParameters;
+
+ /**
+ * The current UBI operation
+ */
+ private final UBI ubi;
+
+ /**
+ * Buffer for single byte update method
+ */
+ private final byte[] singleByte = new byte[1];
+
+ /**
+ * Constructs a Skein engine.
+ *
+ * @param blockSizeBits the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+ * {@link #SKEIN_1024}.
+ * @param outputSizeBits the output/digest size to produce in bits, which must be an integral number of
+ * bytes.
+ */
+ public SkeinEngine(int blockSizeBits, int outputSizeBits)
+ {
+ if (outputSizeBits % 8 != 0)
+ {
+ throw new IllegalArgumentException("Output size must be a multiple of 8 bits. :" + outputSizeBits);
+ }
+ // TODO: Prevent digest sizes > block size?
+ this.outputSizeBytes = outputSizeBits / 8;
+
+ this.threefish = new ThreefishEngine(blockSizeBits);
+ this.ubi = new UBI(threefish.getBlockSize());
+ }
+
+ /**
+ * Creates a SkeinEngine as an exact copy of an existing instance.
+ */
+ public SkeinEngine(SkeinEngine engine)
+ {
+ this(engine.getBlockSize() * 8, engine.getOutputSize() * 8);
+ copyIn(engine);
+ }
+
+ private void copyIn(SkeinEngine engine)
+ {
+ this.ubi.reset(engine.ubi);
+ this.chain = Arrays.clone(engine.chain, this.chain);
+ this.initialState = Arrays.clone(engine.initialState, this.initialState);
+ this.key = Arrays.clone(engine.key, this.key);
+ this.preMessageParameters = clone(engine.preMessageParameters, this.preMessageParameters);
+ this.postMessageParameters = clone(engine.postMessageParameters, this.postMessageParameters);
+ }
+
+ private static Parameter[] clone(Parameter[] data, Parameter[] existing)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ if ((existing == null) || (existing.length != data.length))
+ {
+ existing = new Parameter[data.length];
+ }
+ System.arraycopy(data, 0, existing, 0, existing.length);
+ return existing;
+ }
+
+ public Memoable copy()
+ {
+ return new SkeinEngine(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ SkeinEngine s = (SkeinEngine)other;
+ if ((getBlockSize() != s.getBlockSize()) || (outputSizeBytes != s.outputSizeBytes))
+ {
+ throw new IllegalArgumentException("Incompatible parameters in provided SkeinEngine.");
+ }
+ copyIn(s);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSizeBytes;
+ }
+
+ public int getBlockSize()
+ {
+ return threefish.getBlockSize();
+ }
+
+ /**
+ * Initialises the Skein engine with the provided parameters. See {@link SkeinParameters} for
+ * details on the parameterisation of the Skein hash function.
+ *
+ * @param params the parameters to apply to this engine, or <code>null</code> to use no parameters.
+ */
+ public void init(SkeinParameters params)
+ {
+ this.chain = null;
+ this.key = null;
+ this.preMessageParameters = null;
+ this.postMessageParameters = null;
+
+ if (params != null)
+ {
+ byte[] key = params.getKey();
+ if (key.length < 16)
+ {
+ throw new IllegalArgumentException("Skein key must be at least 128 bits.");
+ }
+ initParams(params.getParameters());
+ }
+ createInitialState();
+
+ // Initialise message block
+ ubiInit(PARAM_TYPE_MESSAGE);
+ }
+
+ private void initParams(Hashtable parameters)
+ {
+ Enumeration keys = parameters.keys();
+ final Vector pre = new Vector();
+ final Vector post = new Vector();
+
+ while (keys.hasMoreElements())
+ {
+ Integer type = (Integer)keys.nextElement();
+ byte[] value = (byte[])parameters.get(type);
+
+ if (type.intValue() == PARAM_TYPE_KEY)
+ {
+ this.key = value;
+ }
+ else if (type.intValue() < PARAM_TYPE_MESSAGE)
+ {
+ pre.addElement(new Parameter(type.intValue(), value));
+ }
+ else
+ {
+ post.addElement(new Parameter(type.intValue(), value));
+ }
+ }
+ preMessageParameters = new Parameter[pre.size()];
+ pre.copyInto(preMessageParameters);
+ sort(preMessageParameters);
+
+ postMessageParameters = new Parameter[post.size()];
+ post.copyInto(postMessageParameters);
+ sort(postMessageParameters);
+ }
+
+ private static void sort(Parameter[] params)
+ {
+ if (params == null)
+ {
+ return;
+ }
+ // Insertion sort, for Java 1.1 compatibility
+ for (int i = 1; i < params.length; i++)
+ {
+ Parameter param = params[i];
+ int hole = i;
+ while (hole > 0 && param.getType() < params[hole - 1].getType())
+ {
+ params[hole] = params[hole - 1];
+ hole = hole - 1;
+ }
+ params[hole] = param;
+ }
+ }
+
+ /**
+ * Calculate the initial (pre message block) chaining state.
+ */
+ private void createInitialState()
+ {
+ long[] precalc = (long[])INITIAL_STATES.get(variantIdentifier(getBlockSize(), getOutputSize()));
+ if ((key == null) && (precalc != null))
+ {
+ // Precalculated UBI(CFG)
+ chain = Arrays.clone(precalc);
+ }
+ else
+ {
+ // Blank initial state
+ chain = new long[getBlockSize() / 8];
+
+ // Process key block
+ if (key != null)
+ {
+ ubiComplete(SkeinParameters.PARAM_TYPE_KEY, key);
+ }
+
+ // Process configuration block
+ ubiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).getBytes());
+ }
+
+ // Process additional pre-message parameters
+ if (preMessageParameters != null)
+ {
+ for (int i = 0; i < preMessageParameters.length; i++)
+ {
+ Parameter param = preMessageParameters[i];
+ ubiComplete(param.getType(), param.getValue());
+ }
+ }
+ initialState = Arrays.clone(chain);
+ }
+
+ /**
+ * Reset the engine to the initial state (with the key and any pre-message parameters , ready to
+ * accept message input.
+ */
+ public void reset()
+ {
+ System.arraycopy(initialState, 0, chain, 0, chain.length);
+
+ ubiInit(PARAM_TYPE_MESSAGE);
+ }
+
+ private void ubiComplete(int type, byte[] value)
+ {
+ ubiInit(type);
+ this.ubi.update(value, 0, value.length, chain);
+ ubiFinal();
+ }
+
+ private void ubiInit(int type)
+ {
+ this.ubi.reset(type);
+ }
+
+ private void ubiFinal()
+ {
+ ubi.doFinal(chain);
+ }
+
+ private void checkInitialised()
+ {
+ if (this.ubi == null)
+ {
+ throw new IllegalArgumentException("Skein engine is not initialised.");
+ }
+ }
+
+ public void update(byte in)
+ {
+ singleByte[0] = in;
+ update(singleByte, 0, 1);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ checkInitialised();
+ ubi.update(in, inOff, len, chain);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ checkInitialised();
+ if (out.length < (outOff + outputSizeBytes))
+ {
+ throw new DataLengthException("Output buffer is too short to hold output of " + outputSizeBytes + " bytes");
+ }
+
+ // Finalise message block
+ ubiFinal();
+
+ // Process additional post-message parameters
+ if (postMessageParameters != null)
+ {
+ for (int i = 0; i < postMessageParameters.length; i++)
+ {
+ Parameter param = postMessageParameters[i];
+ ubiComplete(param.getType(), param.getValue());
+ }
+ }
+
+ // Perform the output transform
+ final int blockSize = getBlockSize();
+ final int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize);
+ for (int i = 0; i < blocksRequired; i++)
+ {
+ final int toWrite = Math.min(blockSize, outputSizeBytes - (i * blockSize));
+ output(i, out, outOff + (i * blockSize), toWrite);
+ }
+
+ reset();
+
+ return outputSizeBytes;
+ }
+
+ private void output(long outputSequence, byte[] out, int outOff, int outputBytes)
+ {
+ byte[] currentBytes = new byte[8];
+ ThreefishEngine.wordToBytes(outputSequence, currentBytes, 0);
+
+ // Output is a sequence of UBI invocations all of which use and preserve the pre-output
+ // state
+ long[] outputWords = new long[chain.length];
+ ubiInit(PARAM_TYPE_OUTPUT);
+ this.ubi.update(currentBytes, 0, currentBytes.length, outputWords);
+ ubi.doFinal(outputWords);
+
+ final int wordsRequired = ((outputBytes + 8 - 1) / 8);
+ for (int i = 0; i < wordsRequired; i++)
+ {
+ int toWrite = Math.min(8, outputBytes - (i * 8));
+ if (toWrite == 8)
+ {
+ ThreefishEngine.wordToBytes(outputWords[i], out, outOff + (i * 8));
+ }
+ else
+ {
+ ThreefishEngine.wordToBytes(outputWords[i], currentBytes, 0);
+ System.arraycopy(currentBytes, 0, out, outOff + (i * 8), toWrite);
+ }
+ }
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html
deleted file mode 100644
index 0a0d95c..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Message digest classes.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
index c8c548e..19c0beb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
@@ -43,6 +43,6 @@ public class ECElGamalDecryptor
ECPoint tmp = pair.getX().multiply(key.getD());
- return pair.getY().add(tmp.negate());
+ return pair.getY().add(tmp.negate()).normalize();
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
index e5569a8..2a0b78d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
@@ -6,7 +6,6 @@ import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.math.ec.ECConstants;
import org.bouncycastle.math.ec.ECPoint;
/**
@@ -69,6 +68,6 @@ public class ECElGamalEncryptor
ECPoint gamma = g.multiply(k);
ECPoint phi = key.getQ().multiply(k).add(point);
- return new ECPair(gamma, phi);
+ return new ECPair(gamma.normalize(), phi.normalize());
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
new file mode 100644
index 0000000..e35e077
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * this transforms the original randomness used for an ElGamal encryption by a fixed value.
+ */
+public class ECFixedTransform
+ implements ECPairFactorTransform
+{
+ private ECPublicKeyParameters key;
+
+ private BigInteger k;
+
+ public ECFixedTransform(BigInteger k)
+ {
+ this.k = k;
+ }
+
+ /**
+ * initialise the underlying EC ElGamal engine.
+ *
+ * @param param the necessary EC key parameters.
+ */
+ public void init(
+ CipherParameters param)
+ {
+ if (!(param instanceof ECPublicKeyParameters))
+ {
+ throw new IllegalArgumentException("ECPublicKeyParameters are required for fixed transform.");
+ }
+
+ this.key = (ECPublicKeyParameters)param;
+ }
+
+ /**
+ * Transform an existing cipher test pair using the ElGamal algorithm. Note: it is assumed this
+ * transform has been initialised with the same public key that was used to create the original
+ * cipher text.
+ *
+ * @param cipherText the EC point to process.
+ * @return returns a new ECPair representing the result of the process.
+ */
+ public ECPair transform(ECPair cipherText)
+ {
+ if (key == null)
+ {
+ throw new IllegalStateException("ECFixedTransform not initialised");
+ }
+
+ ECPoint g = key.getParameters().getG();
+ ECPoint gamma = g.multiply(k);
+ ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
+
+ return new ECPair(cipherText.getX().add(gamma).normalize(), phi.normalize());
+ }
+
+ /**
+ * Return the last transform value used by the transform
+ *
+ * @return a BigInteger representing k value.
+ */
+ public BigInteger getTransformValue()
+ {
+ return k;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
index 32ba070..74016c1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
@@ -69,6 +69,6 @@ public class ECNewPublicKeyTransform
ECPoint gamma = g.multiply(k);
ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
- return new ECPair(gamma, phi);
+ return new ECPair(gamma.normalize(), phi.normalize());
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
index b037984..b293759 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
@@ -12,11 +12,13 @@ import org.bouncycastle.math.ec.ECPoint;
* this transforms the original randomness used for an ElGamal encryption.
*/
public class ECNewRandomnessTransform
- implements ECPairTransform
+ implements ECPairFactorTransform
{
private ECPublicKeyParameters key;
private SecureRandom random;
+ private BigInteger lastK;
+
/**
* initialise the underlying EC ElGamal engine.
*
@@ -71,6 +73,18 @@ public class ECNewRandomnessTransform
ECPoint gamma = g.multiply(k);
ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
- return new ECPair(cipherText.getX().add(gamma), phi);
+ lastK = k;
+
+ return new ECPair(cipherText.getX().add(gamma).normalize(), phi.normalize());
+ }
+
+ /**
+ * Return the last random value generated for a transform
+ *
+ * @return a BigInteger representing the last random value.
+ */
+ public BigInteger getTransformValue()
+ {
+ return lastK;
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java
index d910f3c..ea3b4b9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java
@@ -23,16 +23,18 @@ public class ECPair
return y;
}
- public byte[] getEncoded()
+ public boolean equals(ECPair other)
{
- byte[] xEnc = x.getEncoded();
- byte[] yEnc = y.getEncoded();
-
- byte[] full = new byte[xEnc.length + yEnc.length];
+ return other.getX().equals(getX()) && other.getY().equals(getY());
+ }
- System.arraycopy(xEnc, 0, full, 0, xEnc.length);
- System.arraycopy(yEnc, 0, full, xEnc.length, yEnc.length);
+ public boolean equals(Object other)
+ {
+ return other instanceof ECPair ? equals((ECPair)other) : false;
+ }
- return full;
+ public int hashCode()
+ {
+ return x.hashCode() + 37 * y.hashCode();
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java
new file mode 100644
index 0000000..be48551
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+
+public interface ECPairFactorTransform
+ extends ECPairTransform
+{
+ /**
+ * Return the last value used to calculated a transform.
+ *
+ * @return a BigInteger representing the last transform value used.
+ */
+ BigInteger getTransformValue();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html
deleted file mode 100644
index d50edcf..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Lightweight EC point operations, such as EC ElGamal and randomness transforms.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html
deleted file mode 100644
index fc56f63..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Block encodings for asymmetric ciphers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
new file mode 100644
index 0000000..2d1de39
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
@@ -0,0 +1,186 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.util.Pack;
+
+/**
+ * Implementation of Daniel J. Bernstein's ChaCha stream cipher.
+ */
+public class ChaChaEngine extends Salsa20Engine
+{
+
+ /**
+ * Creates a 20 rounds ChaCha engine.
+ */
+ public ChaChaEngine()
+ {
+ super();
+ }
+
+ /**
+ * Creates a ChaCha engine with a specific number of rounds.
+ * @param rounds the number of rounds (must be an even number).
+ */
+ public ChaChaEngine(int rounds)
+ {
+ super(rounds);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "ChaCha" + rounds;
+ }
+
+ protected void advanceCounter()
+ {
+ if (++engineState[12] == 0)
+ {
+ ++engineState[13];
+ }
+ }
+
+ protected void resetCounter()
+ {
+ engineState[12] = engineState[13] = 0;
+ }
+
+ protected void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ if ((keyBytes.length != 16) && (keyBytes.length != 32))
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+ }
+
+ int offset = 0;
+ byte[] constants;
+
+ // Key
+ engineState[4] = Pack.littleEndianToInt(keyBytes, 0);
+ engineState[5] = Pack.littleEndianToInt(keyBytes, 4);
+ engineState[6] = Pack.littleEndianToInt(keyBytes, 8);
+ engineState[7] = Pack.littleEndianToInt(keyBytes, 12);
+
+ if (keyBytes.length == 32)
+ {
+ constants = sigma;
+ offset = 16;
+ } else
+ {
+ constants = tau;
+ }
+
+ engineState[8] = Pack.littleEndianToInt(keyBytes, offset);
+ engineState[9] = Pack.littleEndianToInt(keyBytes, offset + 4);
+ engineState[10] = Pack.littleEndianToInt(keyBytes, offset + 8);
+ engineState[11] = Pack.littleEndianToInt(keyBytes, offset + 12);
+
+ engineState[0] = Pack.littleEndianToInt(constants, 0);
+ engineState[1] = Pack.littleEndianToInt(constants, 4);
+ engineState[2] = Pack.littleEndianToInt(constants, 8);
+ engineState[3] = Pack.littleEndianToInt(constants, 12);
+
+ // Counter
+ engineState[12] = engineState[13] = 0;
+
+ // IV
+ engineState[14] = Pack.littleEndianToInt(ivBytes, 0);
+ engineState[15] = Pack.littleEndianToInt(ivBytes, 4);
+ }
+
+ protected void generateKeyStream(byte[] output)
+ {
+ chachaCore(rounds, engineState, x);
+ Pack.intToLittleEndian(x, output, 0);
+ }
+
+ /**
+ * ChacCha function
+ *
+ * @param input input data
+ *
+ * @return keystream
+ */
+ public static void chachaCore(int rounds, int[] input, int[] x)
+ {
+ if (input.length != 16) {
+ throw new IllegalArgumentException();
+ }
+ if (x.length != 16) {
+ throw new IllegalArgumentException();
+ }
+ if (rounds % 2 != 0) {
+ throw new IllegalArgumentException("Number of rounds must be even");
+ }
+
+ int x00 = input[ 0];
+ int x01 = input[ 1];
+ int x02 = input[ 2];
+ int x03 = input[ 3];
+ int x04 = input[ 4];
+ int x05 = input[ 5];
+ int x06 = input[ 6];
+ int x07 = input[ 7];
+ int x08 = input[ 8];
+ int x09 = input[ 9];
+ int x10 = input[10];
+ int x11 = input[11];
+ int x12 = input[12];
+ int x13 = input[13];
+ int x14 = input[14];
+ int x15 = input[15];
+
+ for (int i = rounds; i > 0; i -= 2)
+ {
+ x00 += x04; x12 = rotl(x12 ^ x00, 16);
+ x08 += x12; x04 = rotl(x04 ^ x08, 12);
+ x00 += x04; x12 = rotl(x12 ^ x00, 8);
+ x08 += x12; x04 = rotl(x04 ^ x08, 7);
+ x01 += x05; x13 = rotl(x13 ^ x01, 16);
+ x09 += x13; x05 = rotl(x05 ^ x09, 12);
+ x01 += x05; x13 = rotl(x13 ^ x01, 8);
+ x09 += x13; x05 = rotl(x05 ^ x09, 7);
+ x02 += x06; x14 = rotl(x14 ^ x02, 16);
+ x10 += x14; x06 = rotl(x06 ^ x10, 12);
+ x02 += x06; x14 = rotl(x14 ^ x02, 8);
+ x10 += x14; x06 = rotl(x06 ^ x10, 7);
+ x03 += x07; x15 = rotl(x15 ^ x03, 16);
+ x11 += x15; x07 = rotl(x07 ^ x11, 12);
+ x03 += x07; x15 = rotl(x15 ^ x03, 8);
+ x11 += x15; x07 = rotl(x07 ^ x11, 7);
+ x00 += x05; x15 = rotl(x15 ^ x00, 16);
+ x10 += x15; x05 = rotl(x05 ^ x10, 12);
+ x00 += x05; x15 = rotl(x15 ^ x00, 8);
+ x10 += x15; x05 = rotl(x05 ^ x10, 7);
+ x01 += x06; x12 = rotl(x12 ^ x01, 16);
+ x11 += x12; x06 = rotl(x06 ^ x11, 12);
+ x01 += x06; x12 = rotl(x12 ^ x01, 8);
+ x11 += x12; x06 = rotl(x06 ^ x11, 7);
+ x02 += x07; x13 = rotl(x13 ^ x02, 16);
+ x08 += x13; x07 = rotl(x07 ^ x08, 12);
+ x02 += x07; x13 = rotl(x13 ^ x02, 8);
+ x08 += x13; x07 = rotl(x07 ^ x08, 7);
+ x03 += x04; x14 = rotl(x14 ^ x03, 16);
+ x09 += x14; x04 = rotl(x04 ^ x09, 12);
+ x03 += x04; x14 = rotl(x14 ^ x03, 8);
+ x09 += x14; x04 = rotl(x04 ^ x09, 7);
+
+ }
+
+ x[ 0] = x00 + input[ 0];
+ x[ 1] = x01 + input[ 1];
+ x[ 2] = x02 + input[ 2];
+ x[ 3] = x03 + input[ 3];
+ x[ 4] = x04 + input[ 4];
+ x[ 5] = x05 + input[ 5];
+ x[ 6] = x06 + input[ 6];
+ x[ 7] = x07 + input[ 7];
+ x[ 8] = x08 + input[ 8];
+ x[ 9] = x09 + input[ 9];
+ x[10] = x10 + input[10];
+ x[11] = x11 + input[11];
+ x[12] = x12 + input[12];
+ x[13] = x13 + input[13];
+ x[14] = x14 + input[14];
+ x[15] = x15 + input[15];
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
index 6b3da1c..89271f0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
@@ -89,8 +89,7 @@ public class Grain128Engine
System.arraycopy(iv, 0, workingIV, 0, iv.length);
System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
- setKey(workingKey, workingIV);
- initGrain();
+ reset();
}
/**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
index c3baaec..782a93c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
@@ -89,8 +89,7 @@ public class Grainv1Engine
System.arraycopy(iv, 0, workingIV, 0, iv.length);
System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
- setKey(workingKey, workingIV);
- initGrain();
+ reset();
}
/**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
index 69da0f0..015e49e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
@@ -118,6 +118,7 @@ public class HC128Engine
"The key must be 128 bits long");
}
+ idx = 0;
cnt = 0;
int[] w = new int[1280];
@@ -246,7 +247,6 @@ public class HC128Engine
public void reset()
{
- idx = 0;
init();
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
index 538d244..8bd7e9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
@@ -99,6 +99,7 @@ public class HC256Engine
iv = newIV;
}
+ idx = 0;
cnt = 0;
int[] w = new int[2560];
@@ -226,7 +227,6 @@ public class HC256Engine
public void reset()
{
- idx = 0;
init();
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
index 95a395a..1ad2c9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
@@ -12,14 +12,25 @@ import org.bouncycastle.crypto.OutputLengthException;
public class NullEngine implements BlockCipher
{
private boolean initialised;
- protected static final int BLOCK_SIZE = 1;
-
+ protected static final int DEFAULT_BLOCK_SIZE = 1;
+ private final int blockSize;
+
/**
- * Standard constructor.
+ * Constructs a null engine with a block size of 1 byte.
*/
public NullEngine()
{
- super();
+ this(DEFAULT_BLOCK_SIZE);
+ }
+
+ /**
+ * Constructs a null engine with a specific block size.
+ *
+ * @param blockSize the block size in bytes.
+ */
+ public NullEngine(int blockSize)
+ {
+ this.blockSize = blockSize;
}
/* (non-Javadoc)
@@ -44,7 +55,7 @@ public class NullEngine implements BlockCipher
*/
public int getBlockSize()
{
- return BLOCK_SIZE;
+ return blockSize;
}
/* (non-Javadoc)
@@ -57,22 +68,22 @@ public class NullEngine implements BlockCipher
{
throw new IllegalStateException("Null engine not initialised");
}
- if ((inOff + BLOCK_SIZE) > in.length)
- {
- throw new DataLengthException("input buffer too short");
- }
+ if ((inOff + blockSize) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ for (int i = 0; i < blockSize; ++i)
+ {
+ out[outOff + i] = in[inOff + i];
+ }
- if ((outOff + BLOCK_SIZE) > out.length)
- {
- throw new OutputLengthException("output buffer too short");
- }
-
- for (int i = 0; i < BLOCK_SIZE; ++i)
- {
- out[outOff + i] = in[inOff + i];
- }
-
- return BLOCK_SIZE;
+ return blockSize;
}
/* (non-Javadoc)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
index 540bd25..cfd86fb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
@@ -85,7 +85,7 @@ public class RFC3394WrapEngine
byte[] buf = new byte[8 + iv.length];
System.arraycopy(iv, 0, block, 0, iv.length);
- System.arraycopy(in, 0, block, iv.length, inLen);
+ System.arraycopy(in, inOff, block, iv.length, inLen);
engine.init(true, param);
@@ -137,8 +137,8 @@ public class RFC3394WrapEngine
byte[] a = new byte[iv.length];
byte[] buf = new byte[8 + iv.length];
- System.arraycopy(in, 0, a, 0, iv.length);
- System.arraycopy(in, iv.length, block, 0, inLen - iv.length);
+ System.arraycopy(in, inOff, a, 0, iv.length);
+ System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
engine.init(false, param);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
index e7fb943..c9765bf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
@@ -1,5 +1,8 @@
package org.bouncycastle.crypto.engines;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
@@ -8,16 +11,13 @@ import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.util.BigIntegers;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
/**
* this does your basic RSA algorithm with blinding
*/
public class RSABlindedEngine
implements AsymmetricBlockCipher
{
- private static BigInteger ONE = BigInteger.valueOf(1);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
private RSACoreEngine core = new RSACoreEngine();
private RSAKeyParameters key;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
index 6d4210d..2d6140d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
@@ -13,27 +13,28 @@ import org.bouncycastle.util.Strings;
/**
* Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
*/
-
public class Salsa20Engine
implements StreamCipher
{
+ public final static int DEFAULT_ROUNDS = 20;
+
/** Constants */
private final static int STATE_SIZE = 16; // 16, 32 bit ints = 64 bytes
- private final static byte[]
+ protected final static byte[]
sigma = Strings.toByteArray("expand 32-byte k"),
tau = Strings.toByteArray("expand 16-byte k");
+ protected int rounds;
+
/*
* variables to hold the state of the engine
* during encryption and decryption
*/
private int index = 0;
- private int[] engineState = new int[STATE_SIZE]; // state
- private int[] x = new int[STATE_SIZE] ; // internal buffer
- private byte[] keyStream = new byte[STATE_SIZE * 4], // expanded state, 64 bytes
- workingKey = null,
- workingIV = null;
+ protected int[] engineState = new int[STATE_SIZE]; // state
+ protected int[] x = new int[STATE_SIZE] ; // internal buffer
+ private byte[] keyStream = new byte[STATE_SIZE * 4]; // expanded state, 64 bytes
private boolean initialised = false;
/*
@@ -42,6 +43,28 @@ public class Salsa20Engine
private int cW0, cW1, cW2;
/**
+ * Creates a 20 round Salsa20 engine.
+ */
+ public Salsa20Engine()
+ {
+ this(DEFAULT_ROUNDS);
+ }
+
+ /**
+ * Creates a Salsa20 engine with a specific number of rounds.
+ * @param rounds the number of rounds (must be an even number).
+ */
+ public Salsa20Engine(int rounds)
+ {
+ if (rounds <= 0 || (rounds & 1) != 0)
+ {
+ throw new IllegalArgumentException("'rounds' must be a positive, even number");
+ }
+
+ this.rounds = rounds;
+ }
+
+ /**
* initialise a Salsa20 cipher.
*
* @param forEncryption whether or not we are for encryption.
@@ -61,34 +84,43 @@ public class Salsa20Engine
if (!(params instanceof ParametersWithIV))
{
- throw new IllegalArgumentException("Salsa20 Init parameters must include an IV");
+ throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include an IV");
}
ParametersWithIV ivParams = (ParametersWithIV) params;
byte[] iv = ivParams.getIV();
-
- if (iv == null || iv.length != 8)
+ if (iv == null || iv.length != getNonceSize())
{
- throw new IllegalArgumentException("Salsa20 requires exactly 8 bytes of IV");
+ throw new IllegalArgumentException(getAlgorithmName() + " requires exactly " + getNonceSize()
+ + " bytes of IV");
}
if (!(ivParams.getParameters() instanceof KeyParameter))
{
- throw new IllegalArgumentException("Salsa20 Init parameters must include a key");
+ throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include a key");
}
KeyParameter key = (KeyParameter) ivParams.getParameters();
- workingKey = key.getKey();
- workingIV = iv;
+ setKey(key.getKey(), iv);
+ reset();
+ initialised = true;
+ }
- setKey(workingKey, workingIV);
+ protected int getNonceSize()
+ {
+ return 8;
}
public String getAlgorithmName()
{
- return "Salsa20";
+ String name = "Salsa20";
+ if (rounds != DEFAULT_ROUNDS)
+ {
+ name += "/" + rounds;
+ }
+ return name;
}
public byte returnByte(byte in)
@@ -101,11 +133,7 @@ public class Salsa20Engine
if (index == 0)
{
generateKeyStream(keyStream);
-
- if (++engineState[8] == 0)
- {
- ++engineState[9];
- }
+ advanceCounter();
}
byte out = (byte)(keyStream[index]^in);
@@ -114,6 +142,14 @@ public class Salsa20Engine
return out;
}
+ protected void advanceCounter()
+ {
+ if (++engineState[8] == 0)
+ {
+ ++engineState[9];
+ }
+ }
+
public void processBytes(
byte[] in,
int inOff,
@@ -123,7 +159,7 @@ public class Salsa20Engine
{
if (!initialised)
{
- throw new IllegalStateException(getAlgorithmName()+" not initialised");
+ throw new IllegalStateException(getAlgorithmName() + " not initialised");
}
if ((inOff + len) > in.length)
@@ -146,11 +182,7 @@ public class Salsa20Engine
if (index == 0)
{
generateKeyStream(keyStream);
-
- if (++engineState[8] == 0)
- {
- ++engineState[9];
- }
+ advanceCounter();
}
out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]);
@@ -160,28 +192,32 @@ public class Salsa20Engine
public void reset()
{
- setKey(workingKey, workingIV);
+ index = 0;
+ resetLimitCounter();
+ resetCounter();
}
- // Private implementation
-
- private void setKey(byte[] keyBytes, byte[] ivBytes)
+ protected void resetCounter()
{
- workingKey = keyBytes;
- workingIV = ivBytes;
+ engineState[8] = engineState[9] = 0;
+ }
- index = 0;
- resetCounter();
+ protected void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ if ((keyBytes.length != 16) && (keyBytes.length != 32)) {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+ }
+
int offset = 0;
byte[] constants;
// Key
- engineState[1] = Pack.littleEndianToInt(workingKey, 0);
- engineState[2] = Pack.littleEndianToInt(workingKey, 4);
- engineState[3] = Pack.littleEndianToInt(workingKey, 8);
- engineState[4] = Pack.littleEndianToInt(workingKey, 12);
+ engineState[1] = Pack.littleEndianToInt(keyBytes, 0);
+ engineState[2] = Pack.littleEndianToInt(keyBytes, 4);
+ engineState[3] = Pack.littleEndianToInt(keyBytes, 8);
+ engineState[4] = Pack.littleEndianToInt(keyBytes, 12);
- if (workingKey.length == 32)
+ if (keyBytes.length == 32)
{
constants = sigma;
offset = 16;
@@ -191,26 +227,25 @@ public class Salsa20Engine
constants = tau;
}
- engineState[11] = Pack.littleEndianToInt(workingKey, offset);
- engineState[12] = Pack.littleEndianToInt(workingKey, offset+4);
- engineState[13] = Pack.littleEndianToInt(workingKey, offset+8);
- engineState[14] = Pack.littleEndianToInt(workingKey, offset+12);
+ engineState[11] = Pack.littleEndianToInt(keyBytes, offset);
+ engineState[12] = Pack.littleEndianToInt(keyBytes, offset+4);
+ engineState[13] = Pack.littleEndianToInt(keyBytes, offset+8);
+ engineState[14] = Pack.littleEndianToInt(keyBytes, offset+12);
+
engineState[0 ] = Pack.littleEndianToInt(constants, 0);
engineState[5 ] = Pack.littleEndianToInt(constants, 4);
engineState[10] = Pack.littleEndianToInt(constants, 8);
engineState[15] = Pack.littleEndianToInt(constants, 12);
// IV
- engineState[6] = Pack.littleEndianToInt(workingIV, 0);
- engineState[7] = Pack.littleEndianToInt(workingIV, 4);
- engineState[8] = engineState[9] = 0;
-
- initialised = true;
+ engineState[6] = Pack.littleEndianToInt(ivBytes, 0);
+ engineState[7] = Pack.littleEndianToInt(ivBytes, 4);
+ resetCounter();
}
- private void generateKeyStream(byte[] output)
+ protected void generateKeyStream(byte[] output)
{
- salsaCore(20, engineState, x);
+ salsaCore(rounds, engineState, x);
Pack.intToLittleEndian(x, output, 0);
}
@@ -223,50 +258,86 @@ public class Salsa20Engine
*/
public static void salsaCore(int rounds, int[] input, int[] x)
{
- // TODO Exception if rounds odd?
+ if (input.length != 16) {
+ throw new IllegalArgumentException();
+ }
+ if (x.length != 16) {
+ throw new IllegalArgumentException();
+ }
+ if (rounds % 2 != 0) {
+ throw new IllegalArgumentException("Number of rounds must be even");
+ }
- System.arraycopy(input, 0, x, 0, input.length);
+ int x00 = input[ 0];
+ int x01 = input[ 1];
+ int x02 = input[ 2];
+ int x03 = input[ 3];
+ int x04 = input[ 4];
+ int x05 = input[ 5];
+ int x06 = input[ 6];
+ int x07 = input[ 7];
+ int x08 = input[ 8];
+ int x09 = input[ 9];
+ int x10 = input[10];
+ int x11 = input[11];
+ int x12 = input[12];
+ int x13 = input[13];
+ int x14 = input[14];
+ int x15 = input[15];
for (int i = rounds; i > 0; i -= 2)
{
- x[ 4] ^= rotl((x[ 0]+x[12]), 7);
- x[ 8] ^= rotl((x[ 4]+x[ 0]), 9);
- x[12] ^= rotl((x[ 8]+x[ 4]),13);
- x[ 0] ^= rotl((x[12]+x[ 8]),18);
- x[ 9] ^= rotl((x[ 5]+x[ 1]), 7);
- x[13] ^= rotl((x[ 9]+x[ 5]), 9);
- x[ 1] ^= rotl((x[13]+x[ 9]),13);
- x[ 5] ^= rotl((x[ 1]+x[13]),18);
- x[14] ^= rotl((x[10]+x[ 6]), 7);
- x[ 2] ^= rotl((x[14]+x[10]), 9);
- x[ 6] ^= rotl((x[ 2]+x[14]),13);
- x[10] ^= rotl((x[ 6]+x[ 2]),18);
- x[ 3] ^= rotl((x[15]+x[11]), 7);
- x[ 7] ^= rotl((x[ 3]+x[15]), 9);
- x[11] ^= rotl((x[ 7]+x[ 3]),13);
- x[15] ^= rotl((x[11]+x[ 7]),18);
- x[ 1] ^= rotl((x[ 0]+x[ 3]), 7);
- x[ 2] ^= rotl((x[ 1]+x[ 0]), 9);
- x[ 3] ^= rotl((x[ 2]+x[ 1]),13);
- x[ 0] ^= rotl((x[ 3]+x[ 2]),18);
- x[ 6] ^= rotl((x[ 5]+x[ 4]), 7);
- x[ 7] ^= rotl((x[ 6]+x[ 5]), 9);
- x[ 4] ^= rotl((x[ 7]+x[ 6]),13);
- x[ 5] ^= rotl((x[ 4]+x[ 7]),18);
- x[11] ^= rotl((x[10]+x[ 9]), 7);
- x[ 8] ^= rotl((x[11]+x[10]), 9);
- x[ 9] ^= rotl((x[ 8]+x[11]),13);
- x[10] ^= rotl((x[ 9]+x[ 8]),18);
- x[12] ^= rotl((x[15]+x[14]), 7);
- x[13] ^= rotl((x[12]+x[15]), 9);
- x[14] ^= rotl((x[13]+x[12]),13);
- x[15] ^= rotl((x[14]+x[13]),18);
+ x04 ^= rotl((x00+x12), 7);
+ x08 ^= rotl((x04+x00), 9);
+ x12 ^= rotl((x08+x04),13);
+ x00 ^= rotl((x12+x08),18);
+ x09 ^= rotl((x05+x01), 7);
+ x13 ^= rotl((x09+x05), 9);
+ x01 ^= rotl((x13+x09),13);
+ x05 ^= rotl((x01+x13),18);
+ x14 ^= rotl((x10+x06), 7);
+ x02 ^= rotl((x14+x10), 9);
+ x06 ^= rotl((x02+x14),13);
+ x10 ^= rotl((x06+x02),18);
+ x03 ^= rotl((x15+x11), 7);
+ x07 ^= rotl((x03+x15), 9);
+ x11 ^= rotl((x07+x03),13);
+ x15 ^= rotl((x11+x07),18);
+
+ x01 ^= rotl((x00+x03), 7);
+ x02 ^= rotl((x01+x00), 9);
+ x03 ^= rotl((x02+x01),13);
+ x00 ^= rotl((x03+x02),18);
+ x06 ^= rotl((x05+x04), 7);
+ x07 ^= rotl((x06+x05), 9);
+ x04 ^= rotl((x07+x06),13);
+ x05 ^= rotl((x04+x07),18);
+ x11 ^= rotl((x10+x09), 7);
+ x08 ^= rotl((x11+x10), 9);
+ x09 ^= rotl((x08+x11),13);
+ x10 ^= rotl((x09+x08),18);
+ x12 ^= rotl((x15+x14), 7);
+ x13 ^= rotl((x12+x15), 9);
+ x14 ^= rotl((x13+x12),13);
+ x15 ^= rotl((x14+x13),18);
}
- for (int i = 0; i < STATE_SIZE; ++i)
- {
- x[i] += input[i];
- }
+ x[ 0] = x00 + input[ 0];
+ x[ 1] = x01 + input[ 1];
+ x[ 2] = x02 + input[ 2];
+ x[ 3] = x03 + input[ 3];
+ x[ 4] = x04 + input[ 4];
+ x[ 5] = x05 + input[ 5];
+ x[ 6] = x06 + input[ 6];
+ x[ 7] = x07 + input[ 7];
+ x[ 8] = x08 + input[ 8];
+ x[ 9] = x09 + input[ 9];
+ x[10] = x10 + input[10];
+ x[11] = x11 + input[11];
+ x[12] = x12 + input[12];
+ x[13] = x13 + input[13];
+ x[14] = x14 + input[14];
+ x[15] = x15 + input[15];
}
/**
@@ -277,12 +348,12 @@ public class Salsa20Engine
*
* @return rotated x
*/
- private static int rotl(int x, int y)
+ protected static int rotl(int x, int y)
{
return (x << y) | (x >>> -y);
}
- private void resetCounter()
+ private void resetLimitCounter()
{
cW0 = 0;
cW1 = 0;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java
new file mode 100644
index 0000000..b59205d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java
@@ -0,0 +1,201 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * Block cipher Shacal2, designed by Helena Handschuh and David Naccache,
+ * based on hash function SHA-256,
+ * using SHA-256-Initialization-Values as data and SHA-256-Data as key.
+ * <p>
+ * A description of Shacal can be found at:
+ * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.3.4066
+ * Best known cryptanalytic (Wikipedia 11.2013):
+ * Related-key rectangle attack on 44-rounds (Jiqiang Lu, Jongsung Kim).
+ * Comments are related to SHA-256-Naming as described in FIPS PUB 180-2
+ * </p>
+ */
+public class Shacal2Engine
+ implements BlockCipher
+{
+ private final static int[] K = { // SHA-256-Constants
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+
+ private static final int BLOCK_SIZE = 32;
+ private boolean forEncryption = false;
+ private static final int ROUNDS = 64;
+
+ private int[] workingKey = null; // expanded key: corresponds to the message block W in FIPS PUB 180-2
+
+ public Shacal2Engine()
+ {
+ }
+
+ public void reset()
+ {
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Shacal2";
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void init(boolean _forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ throw new IllegalArgumentException("only simple KeyParameter expected.");
+ }
+ this.forEncryption = _forEncryption;
+ workingKey = new int[64];
+ setKey( ((KeyParameter)params).getKey() );
+ }
+
+ public void setKey(byte[] kb)
+ {
+ if (kb.length == 0 || kb.length > 64 || kb.length < 16 || kb.length % 8 != 0)
+ {
+ throw new IllegalArgumentException("Shacal2-key must be 16 - 64 bytes and multiple of 8");
+ }
+
+ bytes2ints(kb, workingKey, 0, 0);
+
+ for ( int i = 16; i < 64; i++)
+ { // Key-Expansion, implicitly Zero-Padding for 16 > i > kb.length/4
+ workingKey[i] =
+ ( (workingKey[i-2] >>> 17 | workingKey[i-2] << -17) // corresponds to ROTL n(x) of FIPS PUB 180-2
+ ^ (workingKey[i-2] >>> 19 | workingKey[i-2] << -19)
+ ^ (workingKey[i-2] >>> 10) ) // corresponds to sigma1(x)-Function of FIPS PUB 180-2
+ + workingKey[i-7]
+ + ( (workingKey[i-15] >>> 7 | workingKey[i-15] << -7)
+ ^ (workingKey[i-15] >>> 18 | workingKey[i-15] << -18)
+ ^ (workingKey[i-15] >>> 3) ) // corresponds to sigma0(x)-Function of FIPS PUB 180-2
+ + workingKey[i-16];
+ }
+ }
+
+ public void encryptBlock(byte[] in, int inOffset, byte[] out, int outOffset)
+ {
+ int[] block = new int[BLOCK_SIZE / 4];// corresponds to working variables a,b,c,d,e,f,g,h of FIPS PUB 180-2
+ bytes2ints(in, block, inOffset, 0);
+
+ for (int i = 0; i < ROUNDS; i++)
+ {
+ int tmp =
+ (((block[4] >>> 6) | (block[4] << -6))
+ ^ ((block[4] >>> 11) | (block[4] << -11))
+ ^ ((block[4] >>> 25) | (block[4] << -25)))
+ + ((block[4] & block[5]) ^ ((~block[4]) & block[6]))
+ + block[7] + K[i] + workingKey[i]; // corresponds to T1 of FIPS PUB 180-2
+ block[7] = block[6];
+ block[6] = block[5];
+ block[5] = block[4];
+ block[4] = block[3] + tmp;
+ block[3] = block[2];
+ block[2] = block[1];
+ block[1] = block[0];
+ block[0] = tmp
+ + (((block[0] >>> 2) | (block[0] << -2))
+ ^ ((block[0] >>> 13) | (block[0] << -13))
+ ^ ((block[0] >>> 22) | (block[0] << -22)))
+ + ((block[0] & block[2]) ^ (block[0] & block[3]) ^ (block[2] & block[3]));
+ //corresponds to T2 of FIPS PUB 180-2, block[1] and block[2] replaced
+ }
+ ints2bytes(block, out, outOffset);
+ }
+
+ public void decryptBlock(byte[] in, int inOffset, byte[] out, int outOffset)
+ {
+ int[] block = new int[BLOCK_SIZE / 4];
+ bytes2ints(in, block, inOffset, 0);
+ for (int i = ROUNDS - 1; i >-1; i--)
+ {
+ int tmp = block[0] - (((block[1] >>> 2) | (block[1] << -2))
+ ^ ((block[1] >>> 13) | (block[1] << -13))
+ ^ ((block[1] >>> 22) | (block[1] << -22)))
+ - ((block[1] & block[2]) ^ (block[1] & block[3]) ^ (block[2] & block[3])); // T2
+ block[0] = block[1];
+ block[1] = block[2];
+ block[2] = block[3];
+ block[3] = block[4] - tmp;
+ block[4] = block[5];
+ block[5] = block[6];
+ block[6] = block[7];
+ block[7] = tmp - K[i] - workingKey[i]
+ - (((block[4] >>> 6) | (block[4] << -6))
+ ^ ((block[4] >>> 11) | (block[4] << -11))
+ ^ ((block[4] >>> 25) | (block[4] << -25)))
+ - ((block[4] & block[5]) ^ ((~block[4]) & block[6])); // T1
+ }
+ ints2bytes(block, out, outOffset);
+ }
+
+ public int processBlock(byte[] in, int inOffset, byte[] out, int outOffset)
+ throws DataLengthException, IllegalStateException
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Shacal2 not initialised");
+ }
+
+ if ((inOffset + BLOCK_SIZE) > in.length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+
+ if ((outOffset + BLOCK_SIZE) > out.length)
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ encryptBlock(in, inOffset, out, outOffset);
+ }
+ else
+ {
+ decryptBlock(in, inOffset, out, outOffset);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ private void bytes2ints(byte[] bytes, int[] block, int bytesPos, int blockPos)
+ {
+ for (int i = blockPos; i < bytes.length / 4; i++)
+ {
+ block[i] = ((bytes[bytesPos++] & 0xFF) << 24)
+ | ((bytes[bytesPos++] & 0xFF) << 16)
+ | ((bytes[bytesPos++] & 0xFF) << 8)
+ | (bytes[bytesPos++] & 0xFF);
+ }
+ }
+
+ private void ints2bytes(int[] block, byte[] out, int pos)
+ {
+ for (int i = 0; i < block.length; i++)
+ {
+ out[pos++] = (byte)(block[i] >>> 24);
+ out[pos++] = (byte)(block[i] >>> 16);
+ out[pos++] = (byte)(block[i] >>> 8);
+ out[pos++] = (byte)block[i];
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java
index b09f189..ac65443 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java
@@ -105,6 +105,11 @@ public class TEAEngine
private void setKey(
byte[] key)
{
+ if (key.length != 16)
+ {
+ throw new IllegalArgumentException("Key size must be 128 bits.");
+ }
+
_a = bytesToInt(key, 0);
_b = bytesToInt(key, 4);
_c = bytesToInt(key, 8);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
new file mode 100644
index 0000000..74bccfe
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
@@ -0,0 +1,1494 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+
+/**
+ * Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block
+ * sizes.
+ * <p/>
+ * This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST
+ * SHA-3 competition in October 2010.
+ * <p/>
+ * Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p/>
+ * This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables
+ * to speed up key schedule injection. <br>
+ * 2 x block size state is retained by each cipher instance.
+ */
+public class ThreefishEngine
+ implements BlockCipher
+{
+ /**
+ * 256 bit block size - Threefish-256
+ */
+ public static final int BLOCKSIZE_256 = 256;
+ /**
+ * 512 bit block size - Threefish-512
+ */
+ public static final int BLOCKSIZE_512 = 512;
+ /**
+ * 1024 bit block size - Threefish-1024
+ */
+ public static final int BLOCKSIZE_1024 = 1024;
+
+ /**
+ * Size of the tweak in bytes (always 128 bit/16 bytes)
+ */
+ private static final int TWEAK_SIZE_BYTES = 16;
+ private static final int TWEAK_SIZE_WORDS = TWEAK_SIZE_BYTES / 8;
+
+ /**
+ * Rounds in Threefish-256
+ */
+ private static final int ROUNDS_256 = 72;
+ /**
+ * Rounds in Threefish-512
+ */
+ private static final int ROUNDS_512 = 72;
+ /**
+ * Rounds in Threefish-1024
+ */
+ private static final int ROUNDS_1024 = 80;
+
+ /**
+ * Max rounds of any of the variants
+ */
+ private static final int MAX_ROUNDS = ROUNDS_1024;
+
+ /**
+ * Key schedule parity constant
+ */
+ private static final long C_240 = 0x1BD11BDAA9FC1A22L;
+
+ /* Pre-calculated modulo arithmetic tables for key schedule lookups */
+ private static int[] MOD9 = new int[MAX_ROUNDS];
+ private static int[] MOD17 = new int[MOD9.length];
+ private static int[] MOD5 = new int[MOD9.length];
+ private static int[] MOD3 = new int[MOD9.length];
+
+ static
+ {
+ for (int i = 0; i < MOD9.length; i++)
+ {
+ MOD17[i] = i % 17;
+ MOD9[i] = i % 9;
+ MOD5[i] = i % 5;
+ MOD3[i] = i % 3;
+ }
+ }
+
+ /**
+ * Block size in bytes
+ */
+ private int blocksizeBytes;
+
+ /**
+ * Block size in 64 bit words
+ */
+ private int blocksizeWords;
+
+ /**
+ * Buffer for byte oriented processBytes to call internal word API
+ */
+ private long[] currentBlock;
+
+ /**
+ * Tweak bytes (2 byte t1,t2, calculated t3 and repeat of t1,t2 for modulo free lookup
+ */
+ private long[] t = new long[5];
+
+ /**
+ * Key schedule words
+ */
+ private long[] kw;
+
+ /**
+ * The internal cipher implementation (varies by blocksize)
+ */
+ private ThreefishCipher cipher;
+
+ private boolean forEncryption;
+
+ /**
+ * Constructs a new Threefish cipher, with a specified block size.
+ *
+ * @param blocksizeBits the block size in bits, one of {@link #BLOCKSIZE_256}, {@link #BLOCKSIZE_512},
+ * {@link #BLOCKSIZE_1024}.
+ */
+ public ThreefishEngine(final int blocksizeBits)
+ {
+ this.blocksizeBytes = (blocksizeBits / 8);
+ this.blocksizeWords = (this.blocksizeBytes / 8);
+ this.currentBlock = new long[blocksizeWords];
+
+ /*
+ * Provide room for original key words, extended key word and repeat of key words for modulo
+ * free lookup of key schedule words.
+ */
+ this.kw = new long[2 * blocksizeWords + 1];
+
+ switch (blocksizeBits)
+ {
+ case BLOCKSIZE_256:
+ cipher = new Threefish256Cipher(kw, t);
+ break;
+ case BLOCKSIZE_512:
+ cipher = new Threefish512Cipher(kw, t);
+ break;
+ case BLOCKSIZE_1024:
+ cipher = new Threefish1024Cipher(kw, t);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid blocksize - Threefish is defined with block size of 256, 512, or 1024 bits");
+ }
+ }
+
+ /**
+ * Initialise the engine.
+ *
+ * @param params an instance of {@link TweakableBlockCipherParameters}, or {@link KeyParameter} (to
+ * use a 0 tweak)
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ final byte[] keyBytes;
+ final byte[] tweakBytes;
+
+ if (params instanceof TweakableBlockCipherParameters)
+ {
+ TweakableBlockCipherParameters tParams = (TweakableBlockCipherParameters)params;
+ keyBytes = tParams.getKey().getKey();
+ tweakBytes = tParams.getTweak();
+ }
+ else if (params instanceof KeyParameter)
+ {
+ keyBytes = ((KeyParameter)params).getKey();
+ tweakBytes = null;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid parameter passed to Threefish init - "
+ + params.getClass().getName());
+ }
+
+ long[] keyWords = null;
+ long[] tweakWords = null;
+
+ if (keyBytes != null)
+ {
+ if (keyBytes.length != this.blocksizeBytes)
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeBytes
+ + " bytes)");
+ }
+ keyWords = new long[blocksizeWords];
+ for (int i = 0; i < keyWords.length; i++)
+ {
+ keyWords[i] = bytesToWord(keyBytes, i * 8);
+ }
+ }
+ if (tweakBytes != null)
+ {
+ if (tweakBytes.length != TWEAK_SIZE_BYTES)
+ {
+ throw new IllegalArgumentException("Threefish tweak must be " + TWEAK_SIZE_BYTES + " bytes");
+ }
+ tweakWords = new long[]{bytesToWord(tweakBytes, 0), bytesToWord(tweakBytes, 8)};
+ }
+ init(forEncryption, keyWords, tweakWords);
+ }
+
+ /**
+ * Initialise the engine, specifying the key and tweak directly.
+ *
+ * @param forEncryption the cipher mode.
+ * @param key the words of the key, or <code>null</code> to use the current key.
+ * @param tweak the 2 word (128 bit) tweak, or <code>null</code> to use the current tweak.
+ */
+ public void init(boolean forEncryption, final long[] key, final long[] tweak)
+ {
+ this.forEncryption = forEncryption;
+ if (key != null)
+ {
+ setKey(key);
+ }
+ if (tweak != null)
+ {
+ setTweak(tweak);
+ }
+ }
+
+ private void setKey(long[] key)
+ {
+ if (key.length != this.blocksizeWords)
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeWords
+ + " words)");
+ }
+
+ /*
+ * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512,
+ * 20k for 1024).
+ *
+ * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations
+ * used, to avoid expensive mod computations during cipher operation.
+ */
+
+ long knw = C_240;
+ for (int i = 0; i < blocksizeWords; i++)
+ {
+ kw[i] = key[i];
+ knw = knw ^ kw[i];
+ }
+ kw[blocksizeWords] = knw;
+ System.arraycopy(kw, 0, kw, blocksizeWords + 1, blocksizeWords);
+ }
+
+ private void setTweak(long[] tweak)
+ {
+ if (tweak.length != TWEAK_SIZE_WORDS)
+ {
+ throw new IllegalArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words.");
+ }
+
+ /*
+ * Tweak schedule partially repeated to avoid mod computations during cipher operation
+ */
+ t[0] = tweak[0];
+ t[1] = tweak[1];
+ t[2] = t[0] ^ t[1];
+ t[3] = t[0];
+ t[4] = t[1];
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Threefish-" + (blocksizeBytes * 8);
+ }
+
+ public int getBlockSize()
+ {
+ return blocksizeBytes;
+ }
+
+ public void reset()
+ {
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ if ((outOff + blocksizeBytes) > out.length)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if ((inOff + blocksizeBytes) > in.length)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
+ for (int i = 0; i < blocksizeBytes; i += 8)
+ {
+ currentBlock[i >> 3] = bytesToWord(in, inOff + i);
+ }
+ processBlock(this.currentBlock, this.currentBlock);
+ for (int i = 0; i < blocksizeBytes; i += 8)
+ {
+ wordToBytes(this.currentBlock[i >> 3], out, outOff + i);
+ }
+
+ return blocksizeBytes;
+ }
+
+ /**
+ * Process a block of data represented as 64 bit words.
+ *
+ * @param in a block sized buffer of words to process.
+ * @param out a block sized buffer of words to receive the output of the operation.
+ * @return the number of 8 byte words processed (which will be the same as the block size).
+ * @throws DataLengthException if either the input or output is not block sized.
+ * @throws IllegalStateException if this engine is not initialised.
+ */
+ public int processBlock(long[] in, long[] out)
+ throws DataLengthException, IllegalStateException
+ {
+ if (kw[blocksizeWords] == 0)
+ {
+ throw new IllegalStateException("Threefish engine not initialised");
+ }
+
+ if (in.length != blocksizeWords)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+ if (out.length != blocksizeWords)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ cipher.encryptBlock(in, out);
+ }
+ else
+ {
+ cipher.decryptBlock(in, out);
+ }
+
+ return blocksizeWords;
+ }
+
+ /**
+ * Read a single 64 bit word from input in LSB first order.
+ */
+ // At least package protected for efficient access from inner class
+ public static long bytesToWord(final byte[] bytes, final int off)
+ {
+ if ((off + 8) > bytes.length)
+ {
+ // Help the JIT avoid index checks
+ throw new IllegalArgumentException();
+ }
+
+ long word = 0;
+ int index = off;
+
+ word = (bytes[index++] & 0xffL);
+ word |= (bytes[index++] & 0xffL) << 8;
+ word |= (bytes[index++] & 0xffL) << 16;
+ word |= (bytes[index++] & 0xffL) << 24;
+ word |= (bytes[index++] & 0xffL) << 32;
+ word |= (bytes[index++] & 0xffL) << 40;
+ word |= (bytes[index++] & 0xffL) << 48;
+ word |= (bytes[index++] & 0xffL) << 56;
+
+ return word;
+ }
+
+ /**
+ * Write a 64 bit word to output in LSB first order.
+ */
+ // At least package protected for efficient access from inner class
+ public static void wordToBytes(final long word, final byte[] bytes, final int off)
+ {
+ if ((off + 8) > bytes.length)
+ {
+ // Help the JIT avoid index checks
+ throw new IllegalArgumentException();
+ }
+ int index = off;
+
+ bytes[index++] = (byte)word;
+ bytes[index++] = (byte)(word >> 8);
+ bytes[index++] = (byte)(word >> 16);
+ bytes[index++] = (byte)(word >> 24);
+ bytes[index++] = (byte)(word >> 32);
+ bytes[index++] = (byte)(word >> 40);
+ bytes[index++] = (byte)(word >> 48);
+ bytes[index++] = (byte)(word >> 56);
+ }
+
+ /**
+ * Rotate left + xor part of the mix operation.
+ */
+ // Package protected for efficient access from inner class
+ static long rotlXor(long x, int n, long xor)
+ {
+ return ((x << n) | (x >>> -n)) ^ xor;
+ }
+
+ /**
+ * Rotate xor + rotate right part of the unmix operation.
+ */
+ // Package protected for efficient access from inner class
+ static long xorRotr(long x, int n, long xor)
+ {
+ long xored = x ^ xor;
+ return (xored >>> n) | (xored << -n);
+ }
+
+ private static abstract class ThreefishCipher
+ {
+ /**
+ * The extended + repeated tweak words
+ */
+ protected final long[] t;
+ /**
+ * The extended + repeated key words
+ */
+ protected final long[] kw;
+
+ protected ThreefishCipher(final long[] kw, final long[] t)
+ {
+ this.kw = kw;
+ this.t = t;
+ }
+
+ abstract void encryptBlock(long[] block, long[] out);
+
+ abstract void decryptBlock(long[] block, long[] out);
+
+ }
+
+ private static final class Threefish256Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 14, ROTATION_0_1 = 16;
+ private static final int ROTATION_1_0 = 52, ROTATION_1_1 = 57;
+ private static final int ROTATION_2_0 = 23, ROTATION_2_1 = 40;
+ private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 37;
+
+ private static final int ROTATION_4_0 = 25, ROTATION_4_1 = 33;
+ private static final int ROTATION_5_0 = 46, ROTATION_5_1 = 12;
+ private static final int ROTATION_6_0 = 58, ROTATION_6_1 = 22;
+ private static final int ROTATION_7_0 = 32, ROTATION_7_1 = 32;
+
+ public Threefish256Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod5 = MOD5;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 9)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 4 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1] + t[0];
+ b2 += kw[2] + t[1];
+ b3 += kw[3];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 2 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_256 / 4); d += 2)
+ {
+ final int dm5 = mod5[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 2 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_1_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_1_1, b2 += b1);
+
+ b1 = rotlXor(b1, ROTATION_2_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_2_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_3_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_3_1, b2 += b1);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm5];
+ b1 += kw[dm5 + 1] + t[dm3];
+ b2 += kw[dm5 + 2] + t[dm3 + 1];
+ b3 += kw[dm5 + 3] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_5_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_5_1, b2 += b1);
+
+ b1 = rotlXor(b1, ROTATION_6_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_6_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_7_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_7_1, b2 += b1);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm5 + 1];
+ b1 += kw[dm5 + 2] + t[dm3 + 1];
+ b2 += kw[dm5 + 3] + t[dm3 + 2];
+ b3 += kw[dm5 + 4] + d + 1;
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ }
+
+ void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod5 = MOD5;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 9)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+
+ for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm5 = mod5[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm5 + 1];
+ b1 -= kw[dm5 + 2] + t[dm3 + 1];
+ b2 -= kw[dm5 + 3] + t[dm3 + 2];
+ b3 -= kw[dm5 + 4] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+
+ b3 = xorRotr(b3, ROTATION_7_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_7_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_6_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_6_1, b2);
+ b2 -= b3;
+
+ b3 = xorRotr(b3, ROTATION_5_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_5_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm5];
+ b1 -= kw[dm5 + 1] + t[dm3];
+ b2 -= kw[dm5 + 2] + t[dm3 + 1];
+ b3 -= kw[dm5 + 3] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b3 = xorRotr(b3, ROTATION_3_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_3_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_2_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_2_1, b2);
+ b2 -= b3;
+
+ b3 = xorRotr(b3, ROTATION_1_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_1_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1] + t[0];
+ b2 -= kw[2] + t[1];
+ b3 -= kw[3];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ }
+
+ }
+
+ private static final class Threefish512Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37;
+ private static final int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42;
+ private static final int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39;
+ private static final int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56;
+
+ private static final int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24;
+ private static final int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17;
+ private static final int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43;
+ private static final int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22;
+
+ protected Threefish512Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ public void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod9 = MOD9;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 17)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 8 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1];
+ b2 += kw[2];
+ b3 += kw[3];
+ b4 += kw[4];
+ b5 += kw[5] + t[0];
+ b6 += kw[6] + t[1];
+ b7 += kw[7];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_512 / 4); d += 2)
+ {
+ final int dm9 = mod9[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+
+ b1 = rotlXor(b1, ROTATION_1_0, b2 += b1);
+ b7 = rotlXor(b7, ROTATION_1_1, b4 += b7);
+ b5 = rotlXor(b5, ROTATION_1_2, b6 += b5);
+ b3 = rotlXor(b3, ROTATION_1_3, b0 += b3);
+
+ b1 = rotlXor(b1, ROTATION_2_0, b4 += b1);
+ b3 = rotlXor(b3, ROTATION_2_1, b6 += b3);
+ b5 = rotlXor(b5, ROTATION_2_2, b0 += b5);
+ b7 = rotlXor(b7, ROTATION_2_3, b2 += b7);
+
+ b1 = rotlXor(b1, ROTATION_3_0, b6 += b1);
+ b7 = rotlXor(b7, ROTATION_3_1, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_3_2, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_3_3, b4 += b3);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm9];
+ b1 += kw[dm9 + 1];
+ b2 += kw[dm9 + 2];
+ b3 += kw[dm9 + 3];
+ b4 += kw[dm9 + 4];
+ b5 += kw[dm9 + 5] + t[dm3];
+ b6 += kw[dm9 + 6] + t[dm3 + 1];
+ b7 += kw[dm9 + 7] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+
+ b1 = rotlXor(b1, ROTATION_5_0, b2 += b1);
+ b7 = rotlXor(b7, ROTATION_5_1, b4 += b7);
+ b5 = rotlXor(b5, ROTATION_5_2, b6 += b5);
+ b3 = rotlXor(b3, ROTATION_5_3, b0 += b3);
+
+ b1 = rotlXor(b1, ROTATION_6_0, b4 += b1);
+ b3 = rotlXor(b3, ROTATION_6_1, b6 += b3);
+ b5 = rotlXor(b5, ROTATION_6_2, b0 += b5);
+ b7 = rotlXor(b7, ROTATION_6_3, b2 += b7);
+
+ b1 = rotlXor(b1, ROTATION_7_0, b6 += b1);
+ b7 = rotlXor(b7, ROTATION_7_1, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_7_2, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_7_3, b4 += b3);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm9 + 1];
+ b1 += kw[dm9 + 2];
+ b2 += kw[dm9 + 3];
+ b3 += kw[dm9 + 4];
+ b4 += kw[dm9 + 5];
+ b5 += kw[dm9 + 6] + t[dm3 + 1];
+ b6 += kw[dm9 + 7] + t[dm3 + 2];
+ b7 += kw[dm9 + 8] + d + 1;
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ out[4] = b4;
+ out[5] = b5;
+ out[6] = b6;
+ out[7] = b7;
+ }
+
+ public void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod9 = MOD9;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 17)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+
+ for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm9 = mod9[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm9 + 1];
+ b1 -= kw[dm9 + 2];
+ b2 -= kw[dm9 + 3];
+ b3 -= kw[dm9 + 4];
+ b4 -= kw[dm9 + 5];
+ b5 -= kw[dm9 + 6] + t[dm3 + 1];
+ b6 -= kw[dm9 + 7] + t[dm3 + 2];
+ b7 -= kw[dm9 + 8] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+
+ b1 = xorRotr(b1, ROTATION_7_0, b6);
+ b6 -= b1;
+ b7 = xorRotr(b7, ROTATION_7_1, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_7_2, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_7_3, b4);
+ b4 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_6_0, b4);
+ b4 -= b1;
+ b3 = xorRotr(b3, ROTATION_6_1, b6);
+ b6 -= b3;
+ b5 = xorRotr(b5, ROTATION_6_2, b0);
+ b0 -= b5;
+ b7 = xorRotr(b7, ROTATION_6_3, b2);
+ b2 -= b7;
+
+ b1 = xorRotr(b1, ROTATION_5_0, b2);
+ b2 -= b1;
+ b7 = xorRotr(b7, ROTATION_5_1, b4);
+ b4 -= b7;
+ b5 = xorRotr(b5, ROTATION_5_2, b6);
+ b6 -= b5;
+ b3 = xorRotr(b3, ROTATION_5_3, b0);
+ b0 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_4_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_4_3, b6);
+ b6 -= b7;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm9];
+ b1 -= kw[dm9 + 1];
+ b2 -= kw[dm9 + 2];
+ b3 -= kw[dm9 + 3];
+ b4 -= kw[dm9 + 4];
+ b5 -= kw[dm9 + 5] + t[dm3];
+ b6 -= kw[dm9 + 6] + t[dm3 + 1];
+ b7 -= kw[dm9 + 7] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b1 = xorRotr(b1, ROTATION_3_0, b6);
+ b6 -= b1;
+ b7 = xorRotr(b7, ROTATION_3_1, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_3_2, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_3_3, b4);
+ b4 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_2_0, b4);
+ b4 -= b1;
+ b3 = xorRotr(b3, ROTATION_2_1, b6);
+ b6 -= b3;
+ b5 = xorRotr(b5, ROTATION_2_2, b0);
+ b0 -= b5;
+ b7 = xorRotr(b7, ROTATION_2_3, b2);
+ b2 -= b7;
+
+ b1 = xorRotr(b1, ROTATION_1_0, b2);
+ b2 -= b1;
+ b7 = xorRotr(b7, ROTATION_1_1, b4);
+ b4 -= b7;
+ b5 = xorRotr(b5, ROTATION_1_2, b6);
+ b6 -= b5;
+ b3 = xorRotr(b3, ROTATION_1_3, b0);
+ b0 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_0_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_0_3, b6);
+ b6 -= b7;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1];
+ b2 -= kw[2];
+ b3 -= kw[3];
+ b4 -= kw[4];
+ b5 -= kw[5] + t[0];
+ b6 -= kw[6] + t[1];
+ b7 -= kw[7];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ state[4] = b4;
+ state[5] = b5;
+ state[6] = b6;
+ state[7] = b7;
+ }
+ }
+
+ private static final class Threefish1024Cipher
+ extends ThreefishCipher
+ {
+ /**
+ * Mix rotation constants defined in Skein 1.3 specification
+ */
+ private static final int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47;
+ private static final int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37;
+ private static final int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55;
+ private static final int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52;
+ private static final int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13;
+ private static final int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17;
+ private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41;
+ private static final int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25;
+
+ private static final int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31;
+ private static final int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30;
+ private static final int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51;
+ private static final int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41;
+ private static final int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46;
+ private static final int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25;
+ private static final int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52;
+ private static final int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20;
+
+ public Threefish1024Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod17 = MOD17;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 33)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 16 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+ long b8 = block[8];
+ long b9 = block[9];
+ long b10 = block[10];
+ long b11 = block[11];
+ long b12 = block[12];
+ long b13 = block[13];
+ long b14 = block[14];
+ long b15 = block[15];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1];
+ b2 += kw[2];
+ b3 += kw[3];
+ b4 += kw[4];
+ b5 += kw[5];
+ b6 += kw[6];
+ b7 += kw[7];
+ b8 += kw[8];
+ b9 += kw[9];
+ b10 += kw[10];
+ b11 += kw[11];
+ b12 += kw[12];
+ b13 += kw[13] + t[0];
+ b14 += kw[14] + t[1];
+ b15 += kw[15];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_1024 / 4); d += 2)
+ {
+ final int dm17 = mod17[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+ b9 = rotlXor(b9, ROTATION_0_4, b8 += b9);
+ b11 = rotlXor(b11, ROTATION_0_5, b10 += b11);
+ b13 = rotlXor(b13, ROTATION_0_6, b12 += b13);
+ b15 = rotlXor(b15, ROTATION_0_7, b14 += b15);
+
+ b9 = rotlXor(b9, ROTATION_1_0, b0 += b9);
+ b13 = rotlXor(b13, ROTATION_1_1, b2 += b13);
+ b11 = rotlXor(b11, ROTATION_1_2, b6 += b11);
+ b15 = rotlXor(b15, ROTATION_1_3, b4 += b15);
+ b7 = rotlXor(b7, ROTATION_1_4, b10 += b7);
+ b3 = rotlXor(b3, ROTATION_1_5, b12 += b3);
+ b5 = rotlXor(b5, ROTATION_1_6, b14 += b5);
+ b1 = rotlXor(b1, ROTATION_1_7, b8 += b1);
+
+ b7 = rotlXor(b7, ROTATION_2_0, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_2_1, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_2_2, b4 += b3);
+ b1 = rotlXor(b1, ROTATION_2_3, b6 += b1);
+ b15 = rotlXor(b15, ROTATION_2_4, b12 += b15);
+ b13 = rotlXor(b13, ROTATION_2_5, b14 += b13);
+ b11 = rotlXor(b11, ROTATION_2_6, b8 += b11);
+ b9 = rotlXor(b9, ROTATION_2_7, b10 += b9);
+
+ b15 = rotlXor(b15, ROTATION_3_0, b0 += b15);
+ b11 = rotlXor(b11, ROTATION_3_1, b2 += b11);
+ b13 = rotlXor(b13, ROTATION_3_2, b6 += b13);
+ b9 = rotlXor(b9, ROTATION_3_3, b4 += b9);
+ b1 = rotlXor(b1, ROTATION_3_4, b14 += b1);
+ b5 = rotlXor(b5, ROTATION_3_5, b8 += b5);
+ b3 = rotlXor(b3, ROTATION_3_6, b10 += b3);
+ b7 = rotlXor(b7, ROTATION_3_7, b12 += b7);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm17];
+ b1 += kw[dm17 + 1];
+ b2 += kw[dm17 + 2];
+ b3 += kw[dm17 + 3];
+ b4 += kw[dm17 + 4];
+ b5 += kw[dm17 + 5];
+ b6 += kw[dm17 + 6];
+ b7 += kw[dm17 + 7];
+ b8 += kw[dm17 + 8];
+ b9 += kw[dm17 + 9];
+ b10 += kw[dm17 + 10];
+ b11 += kw[dm17 + 11];
+ b12 += kw[dm17 + 12];
+ b13 += kw[dm17 + 13] + t[dm3];
+ b14 += kw[dm17 + 14] + t[dm3 + 1];
+ b15 += kw[dm17 + 15] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+ b9 = rotlXor(b9, ROTATION_4_4, b8 += b9);
+ b11 = rotlXor(b11, ROTATION_4_5, b10 += b11);
+ b13 = rotlXor(b13, ROTATION_4_6, b12 += b13);
+ b15 = rotlXor(b15, ROTATION_4_7, b14 += b15);
+
+ b9 = rotlXor(b9, ROTATION_5_0, b0 += b9);
+ b13 = rotlXor(b13, ROTATION_5_1, b2 += b13);
+ b11 = rotlXor(b11, ROTATION_5_2, b6 += b11);
+ b15 = rotlXor(b15, ROTATION_5_3, b4 += b15);
+ b7 = rotlXor(b7, ROTATION_5_4, b10 += b7);
+ b3 = rotlXor(b3, ROTATION_5_5, b12 += b3);
+ b5 = rotlXor(b5, ROTATION_5_6, b14 += b5);
+ b1 = rotlXor(b1, ROTATION_5_7, b8 += b1);
+
+ b7 = rotlXor(b7, ROTATION_6_0, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_6_1, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_6_2, b4 += b3);
+ b1 = rotlXor(b1, ROTATION_6_3, b6 += b1);
+ b15 = rotlXor(b15, ROTATION_6_4, b12 += b15);
+ b13 = rotlXor(b13, ROTATION_6_5, b14 += b13);
+ b11 = rotlXor(b11, ROTATION_6_6, b8 += b11);
+ b9 = rotlXor(b9, ROTATION_6_7, b10 += b9);
+
+ b15 = rotlXor(b15, ROTATION_7_0, b0 += b15);
+ b11 = rotlXor(b11, ROTATION_7_1, b2 += b11);
+ b13 = rotlXor(b13, ROTATION_7_2, b6 += b13);
+ b9 = rotlXor(b9, ROTATION_7_3, b4 += b9);
+ b1 = rotlXor(b1, ROTATION_7_4, b14 += b1);
+ b5 = rotlXor(b5, ROTATION_7_5, b8 += b5);
+ b3 = rotlXor(b3, ROTATION_7_6, b10 += b3);
+ b7 = rotlXor(b7, ROTATION_7_7, b12 += b7);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm17 + 1];
+ b1 += kw[dm17 + 2];
+ b2 += kw[dm17 + 3];
+ b3 += kw[dm17 + 4];
+ b4 += kw[dm17 + 5];
+ b5 += kw[dm17 + 6];
+ b6 += kw[dm17 + 7];
+ b7 += kw[dm17 + 8];
+ b8 += kw[dm17 + 9];
+ b9 += kw[dm17 + 10];
+ b10 += kw[dm17 + 11];
+ b11 += kw[dm17 + 12];
+ b12 += kw[dm17 + 13];
+ b13 += kw[dm17 + 14] + t[dm3 + 1];
+ b14 += kw[dm17 + 15] + t[dm3 + 2];
+ b15 += kw[dm17 + 16] + d + 1;
+
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ out[4] = b4;
+ out[5] = b5;
+ out[6] = b6;
+ out[7] = b7;
+ out[8] = b8;
+ out[9] = b9;
+ out[10] = b10;
+ out[11] = b11;
+ out[12] = b12;
+ out[13] = b13;
+ out[14] = b14;
+ out[15] = b15;
+ }
+
+ void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod17 = MOD17;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 33)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+ long b8 = block[8];
+ long b9 = block[9];
+ long b10 = block[10];
+ long b11 = block[11];
+ long b12 = block[12];
+ long b13 = block[13];
+ long b14 = block[14];
+ long b15 = block[15];
+
+ for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm17 = mod17[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm17 + 1];
+ b1 -= kw[dm17 + 2];
+ b2 -= kw[dm17 + 3];
+ b3 -= kw[dm17 + 4];
+ b4 -= kw[dm17 + 5];
+ b5 -= kw[dm17 + 6];
+ b6 -= kw[dm17 + 7];
+ b7 -= kw[dm17 + 8];
+ b8 -= kw[dm17 + 9];
+ b9 -= kw[dm17 + 10];
+ b10 -= kw[dm17 + 11];
+ b11 -= kw[dm17 + 12];
+ b12 -= kw[dm17 + 13];
+ b13 -= kw[dm17 + 14] + t[dm3 + 1];
+ b14 -= kw[dm17 + 15] + t[dm3 + 2];
+ b15 -= kw[dm17 + 16] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+ b15 = xorRotr(b15, ROTATION_7_0, b0);
+ b0 -= b15;
+ b11 = xorRotr(b11, ROTATION_7_1, b2);
+ b2 -= b11;
+ b13 = xorRotr(b13, ROTATION_7_2, b6);
+ b6 -= b13;
+ b9 = xorRotr(b9, ROTATION_7_3, b4);
+ b4 -= b9;
+ b1 = xorRotr(b1, ROTATION_7_4, b14);
+ b14 -= b1;
+ b5 = xorRotr(b5, ROTATION_7_5, b8);
+ b8 -= b5;
+ b3 = xorRotr(b3, ROTATION_7_6, b10);
+ b10 -= b3;
+ b7 = xorRotr(b7, ROTATION_7_7, b12);
+ b12 -= b7;
+
+ b7 = xorRotr(b7, ROTATION_6_0, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_6_1, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_6_2, b4);
+ b4 -= b3;
+ b1 = xorRotr(b1, ROTATION_6_3, b6);
+ b6 -= b1;
+ b15 = xorRotr(b15, ROTATION_6_4, b12);
+ b12 -= b15;
+ b13 = xorRotr(b13, ROTATION_6_5, b14);
+ b14 -= b13;
+ b11 = xorRotr(b11, ROTATION_6_6, b8);
+ b8 -= b11;
+ b9 = xorRotr(b9, ROTATION_6_7, b10);
+ b10 -= b9;
+
+ b9 = xorRotr(b9, ROTATION_5_0, b0);
+ b0 -= b9;
+ b13 = xorRotr(b13, ROTATION_5_1, b2);
+ b2 -= b13;
+ b11 = xorRotr(b11, ROTATION_5_2, b6);
+ b6 -= b11;
+ b15 = xorRotr(b15, ROTATION_5_3, b4);
+ b4 -= b15;
+ b7 = xorRotr(b7, ROTATION_5_4, b10);
+ b10 -= b7;
+ b3 = xorRotr(b3, ROTATION_5_5, b12);
+ b12 -= b3;
+ b5 = xorRotr(b5, ROTATION_5_6, b14);
+ b14 -= b5;
+ b1 = xorRotr(b1, ROTATION_5_7, b8);
+ b8 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_4_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_4_3, b6);
+ b6 -= b7;
+ b9 = xorRotr(b9, ROTATION_4_4, b8);
+ b8 -= b9;
+ b11 = xorRotr(b11, ROTATION_4_5, b10);
+ b10 -= b11;
+ b13 = xorRotr(b13, ROTATION_4_6, b12);
+ b12 -= b13;
+ b15 = xorRotr(b15, ROTATION_4_7, b14);
+ b14 -= b15;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm17];
+ b1 -= kw[dm17 + 1];
+ b2 -= kw[dm17 + 2];
+ b3 -= kw[dm17 + 3];
+ b4 -= kw[dm17 + 4];
+ b5 -= kw[dm17 + 5];
+ b6 -= kw[dm17 + 6];
+ b7 -= kw[dm17 + 7];
+ b8 -= kw[dm17 + 8];
+ b9 -= kw[dm17 + 9];
+ b10 -= kw[dm17 + 10];
+ b11 -= kw[dm17 + 11];
+ b12 -= kw[dm17 + 12];
+ b13 -= kw[dm17 + 13] + t[dm3];
+ b14 -= kw[dm17 + 14] + t[dm3 + 1];
+ b15 -= kw[dm17 + 15] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b15 = xorRotr(b15, ROTATION_3_0, b0);
+ b0 -= b15;
+ b11 = xorRotr(b11, ROTATION_3_1, b2);
+ b2 -= b11;
+ b13 = xorRotr(b13, ROTATION_3_2, b6);
+ b6 -= b13;
+ b9 = xorRotr(b9, ROTATION_3_3, b4);
+ b4 -= b9;
+ b1 = xorRotr(b1, ROTATION_3_4, b14);
+ b14 -= b1;
+ b5 = xorRotr(b5, ROTATION_3_5, b8);
+ b8 -= b5;
+ b3 = xorRotr(b3, ROTATION_3_6, b10);
+ b10 -= b3;
+ b7 = xorRotr(b7, ROTATION_3_7, b12);
+ b12 -= b7;
+
+ b7 = xorRotr(b7, ROTATION_2_0, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_2_1, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_2_2, b4);
+ b4 -= b3;
+ b1 = xorRotr(b1, ROTATION_2_3, b6);
+ b6 -= b1;
+ b15 = xorRotr(b15, ROTATION_2_4, b12);
+ b12 -= b15;
+ b13 = xorRotr(b13, ROTATION_2_5, b14);
+ b14 -= b13;
+ b11 = xorRotr(b11, ROTATION_2_6, b8);
+ b8 -= b11;
+ b9 = xorRotr(b9, ROTATION_2_7, b10);
+ b10 -= b9;
+
+ b9 = xorRotr(b9, ROTATION_1_0, b0);
+ b0 -= b9;
+ b13 = xorRotr(b13, ROTATION_1_1, b2);
+ b2 -= b13;
+ b11 = xorRotr(b11, ROTATION_1_2, b6);
+ b6 -= b11;
+ b15 = xorRotr(b15, ROTATION_1_3, b4);
+ b4 -= b15;
+ b7 = xorRotr(b7, ROTATION_1_4, b10);
+ b10 -= b7;
+ b3 = xorRotr(b3, ROTATION_1_5, b12);
+ b12 -= b3;
+ b5 = xorRotr(b5, ROTATION_1_6, b14);
+ b14 -= b5;
+ b1 = xorRotr(b1, ROTATION_1_7, b8);
+ b8 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_0_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_0_3, b6);
+ b6 -= b7;
+ b9 = xorRotr(b9, ROTATION_0_4, b8);
+ b8 -= b9;
+ b11 = xorRotr(b11, ROTATION_0_5, b10);
+ b10 -= b11;
+ b13 = xorRotr(b13, ROTATION_0_6, b12);
+ b12 -= b13;
+ b15 = xorRotr(b15, ROTATION_0_7, b14);
+ b14 -= b15;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1];
+ b2 -= kw[2];
+ b3 -= kw[3];
+ b4 -= kw[4];
+ b5 -= kw[5];
+ b6 -= kw[6];
+ b7 -= kw[7];
+ b8 -= kw[8];
+ b9 -= kw[9];
+ b10 -= kw[10];
+ b11 -= kw[11];
+ b12 -= kw[12];
+ b13 -= kw[13] + t[0];
+ b14 -= kw[14] + t[1];
+ b15 -= kw[15];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ state[4] = b4;
+ state[5] = b5;
+ state[6] = b6;
+ state[7] = b7;
+ state[8] = b8;
+ state[9] = b9;
+ state[10] = b10;
+ state[11] = b11;
+ state[12] = b12;
+ state[13] = b13;
+ state[14] = b14;
+ state[15] = b15;
+ }
+
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
index 0703fd6..f16f6d4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
@@ -44,7 +44,6 @@ public class VMPCEngine implements StreamCipher
}
ParametersWithIV ivParams = (ParametersWithIV) params;
- KeyParameter key = (KeyParameter) ivParams.getParameters();
if (!(ivParams.getParameters() instanceof KeyParameter))
{
@@ -52,6 +51,8 @@ public class VMPCEngine implements StreamCipher
"VMPC init parameters must include a key");
}
+ KeyParameter key = (KeyParameter) ivParams.getParameters();
+
this.workingIV = ivParams.getIV();
if (workingIV == null || workingIV.length < 1 || workingIV.length > 768)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java
new file mode 100644
index 0000000..5b2181d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.util.Pack;
+
+/**
+ * Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce.
+ * <p>
+ * XSalsa20 requires a 256 bit key, and a 192 bit nonce.
+ */
+public class XSalsa20Engine extends Salsa20Engine
+{
+
+ public String getAlgorithmName()
+ {
+ return "XSalsa20";
+ }
+
+ protected int getNonceSize()
+ {
+ return 24;
+ }
+
+ /**
+ * XSalsa20 key generation: process 256 bit input key and 128 bits of the input nonce
+ * using a core Salsa20 function without input addition to produce 256 bit working key
+ * and use that with the remaining 64 bits of nonce to initialize a standard Salsa20 engine state.
+ */
+ protected void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ if (keyBytes.length != 32)
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires a 256 bit key");
+ }
+
+ // Set key for HSalsa20
+ super.setKey(keyBytes, ivBytes);
+
+ // Pack next 64 bits of IV into engine state instead of counter
+ engineState[8] = Pack.littleEndianToInt(ivBytes, 8);
+ engineState[9] = Pack.littleEndianToInt(ivBytes, 12);
+
+ // Process engine state to generate Salsa20 key
+ int[] hsalsa20Out = new int[engineState.length];
+ salsaCore(20, engineState, hsalsa20Out);
+
+ // Set new key, removing addition in last round of salsaCore
+ engineState[1] = hsalsa20Out[0] - engineState[0];
+ engineState[2] = hsalsa20Out[5] - engineState[5];
+ engineState[3] = hsalsa20Out[10] - engineState[10];
+ engineState[4] = hsalsa20Out[15] - engineState[15];
+
+ engineState[11] = hsalsa20Out[6] - engineState[6];
+ engineState[12] = hsalsa20Out[7] - engineState[7];
+ engineState[13] = hsalsa20Out[8] - engineState[8];
+ engineState[14] = hsalsa20Out[9] - engineState[9];
+
+ // Last 64 bits of input IV
+ engineState[6] = Pack.littleEndianToInt(ivBytes, 16);
+ engineState[7] = Pack.littleEndianToInt(ivBytes, 20);
+
+ // Counter reset
+ resetCounter();
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java
index f037da4..fb21bbc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java
@@ -107,6 +107,11 @@ public class XTEAEngine
private void setKey(
byte[] key)
{
+ if (key.length != 16)
+ {
+ throw new IllegalArgumentException("Key size must be 128 bits.");
+ }
+
int i, j;
for (i = j = 0; i < 4; i++,j+=4)
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html
deleted file mode 100644
index e945dac..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Basic cipher classes.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html
deleted file mode 100644
index 390a540..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Simple examples of light weight API usage.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
index 2ef8dd2..16c8b91 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
@@ -1,9 +1,9 @@
package org.bouncycastle.crypto.generators;
import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
import org.bouncycastle.crypto.DerivationParameters;
import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.DigestDerivationFunction;
import org.bouncycastle.crypto.params.ISO18033KDFParameters;
import org.bouncycastle.crypto.params.KDFParameters;
import org.bouncycastle.crypto.util.Pack;
@@ -13,7 +13,8 @@ import org.bouncycastle.crypto.util.Pack;
* 18033 <br>
* This implementation is based on ISO 18033/P1363a.
*/
-public class BaseKDFBytesGenerator implements DerivationFunction
+public class BaseKDFBytesGenerator
+ implements DigestDerivationFunction
{
private int counterStart;
private Digest digest;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
index 749b0cc..f7a3df2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
@@ -226,10 +226,10 @@ public class DSAParametersGenerator
int seedlen = N;
byte[] seed = new byte[seedlen / 8];
-// 3. n = ceiling(L ⁄ outlen) – 1.
+// 3. n = ceiling(L / outlen) - 1.
int n = (L - 1) / outlen;
-// 4. b = L – 1 – (n ∗ outlen).
+// 4. b = L - 1 - (n * outlen).
int b = (L - 1) % outlen;
byte[] output = new byte[d.getDigestSize()];
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
index d77bd74..d5f5fc8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
@@ -26,6 +26,11 @@ public class ECKeyPairGenerator
this.random = ecP.getRandom();
this.params = ecP.getDomainParameters();
+
+ if (this.random == null)
+ {
+ this.random = new SecureRandom();
+ }
}
/**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
new file mode 100644
index 0000000..306530e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.MacDerivationFunction;
+import org.bouncycastle.crypto.params.KDFCounterParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFCounterBytesGenerator
+ implements MacDerivationFunction
+{
+
+ private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ // please refer to the standard for the meaning of the variable names
+ // all field lengths are in bytes, not in bits as specified by the standard
+
+ // fields set by the constructor
+ private final Mac prf;
+ private final int h;
+
+ // fields set by init
+ private byte[] fixedInputData;
+ private int maxSizeExcl;
+ // ios is i defined as an octet string (the binary representation)
+ private byte[] ios;
+
+ // operational
+ private int generatedBytes;
+ // k is used as buffer for all K(i) values
+ private byte[] k;
+
+
+ public KDFCounterBytesGenerator(Mac prf)
+ {
+ this.prf = prf;
+ this.h = prf.getMacSize();
+ this.k = new byte[h];
+ }
+
+
+ public void init(DerivationParameters param)
+ {
+ if (!(param instanceof KDFCounterParameters))
+ {
+ throw new IllegalArgumentException("Wrong type of arguments given");
+ }
+
+ KDFCounterParameters kdfParams = (KDFCounterParameters)param;
+
+ // --- init mac based PRF ---
+
+ this.prf.init(new KeyParameter(kdfParams.getKI()));
+
+ // --- set arguments ---
+
+ this.fixedInputData = kdfParams.getFixedInputData();
+
+ int r = kdfParams.getR();
+ this.ios = new byte[r / 8];
+
+ BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+ this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+ Integer.MAX_VALUE : maxSize.intValue();
+
+ // --- set operational state ---
+
+ generatedBytes = 0;
+ }
+
+
+ public Mac getMac()
+ {
+ return prf;
+ }
+
+ public int generateBytes(byte[] out, int outOff, int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+
+ int generatedBytesAfter = generatedBytes + len;
+ if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+ {
+ throw new DataLengthException(
+ "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+ }
+
+ if (generatedBytes % h == 0)
+ {
+ generateNext();
+ }
+
+ // copy what is left in the currentT (1..hash
+ int toGenerate = len;
+ int posInK = generatedBytes % h;
+ int leftInK = h - generatedBytes % h;
+ int toCopy = Math.min(leftInK, toGenerate);
+ System.arraycopy(k, posInK, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+
+ while (toGenerate > 0)
+ {
+ generateNext();
+ toCopy = Math.min(h, toGenerate);
+ System.arraycopy(k, 0, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+ }
+
+ return len;
+ }
+
+ private void generateNext()
+ {
+ int i = generatedBytes / h + 1;
+
+ // encode i into counter buffer
+ switch (ios.length)
+ {
+ case 4:
+ ios[0] = (byte)(i >>> 24);
+ // fall through
+ case 3:
+ ios[ios.length - 3] = (byte)(i >>> 16);
+ // fall through
+ case 2:
+ ios[ios.length - 2] = (byte)(i >>> 8);
+ // fall through
+ case 1:
+ ios[ios.length - 1] = (byte)i;
+ break;
+ default:
+ throw new IllegalStateException("Unsupported size of counter i");
+ }
+
+
+ // special case for K(0): K(0) is empty, so no update
+ prf.update(ios, 0, ios.length);
+ prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.doFinal(k, 0);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java
new file mode 100644
index 0000000..6115a1a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.MacDerivationFunction;
+import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFDoublePipelineIterationBytesGenerator
+ implements MacDerivationFunction
+{
+
+ private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ // please refer to the standard for the meaning of the variable names
+ // all field lengths are in bytes, not in bits as specified by the standard
+
+ // fields set by the constructor
+ private final Mac prf;
+ private final int h;
+
+ // fields set by init
+ private byte[] fixedInputData;
+ private int maxSizeExcl;
+ // ios is i defined as an octet string (the binary representation)
+ private byte[] ios;
+ private boolean useCounter;
+
+ // operational
+ private int generatedBytes;
+ // k is used as buffer for all K(i) values
+ private byte[] a;
+ private byte[] k;
+
+
+ public KDFDoublePipelineIterationBytesGenerator(Mac prf)
+ {
+ this.prf = prf;
+ this.h = prf.getMacSize();
+ this.a = new byte[h];
+ this.k = new byte[h];
+ }
+
+ public void init(DerivationParameters params)
+ {
+ if (!(params instanceof KDFDoublePipelineIterationParameters))
+ {
+ throw new IllegalArgumentException("Wrong type of arguments given");
+ }
+
+ KDFDoublePipelineIterationParameters dpiParams = (KDFDoublePipelineIterationParameters)params;
+
+ // --- init mac based PRF ---
+
+ this.prf.init(new KeyParameter(dpiParams.getKI()));
+
+ // --- set arguments ---
+
+ this.fixedInputData = dpiParams.getFixedInputData();
+
+ int r = dpiParams.getR();
+ this.ios = new byte[r / 8];
+
+ if (dpiParams.useCounter())
+ {
+ // this is more conservative than the spec
+ BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+ this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+ Integer.MAX_VALUE : maxSize.intValue();
+ }
+ else
+ {
+ this.maxSizeExcl = Integer.MAX_VALUE;
+ }
+
+ this.useCounter = dpiParams.useCounter();
+
+ // --- set operational state ---
+
+ generatedBytes = 0;
+ }
+
+ public Mac getMac()
+ {
+ return prf;
+ }
+
+ public int generateBytes(byte[] out, int outOff, int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+
+ int generatedBytesAfter = generatedBytes + len;
+ if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+ {
+ throw new DataLengthException(
+ "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+ }
+
+ if (generatedBytes % h == 0)
+ {
+ generateNext();
+ }
+
+ // copy what is left in the currentT (1..hash
+ int toGenerate = len;
+ int posInK = generatedBytes % h;
+ int leftInK = h - generatedBytes % h;
+ int toCopy = Math.min(leftInK, toGenerate);
+ System.arraycopy(k, posInK, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+
+ while (toGenerate > 0)
+ {
+ generateNext();
+ toCopy = Math.min(h, toGenerate);
+ System.arraycopy(k, 0, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+ }
+
+ return len;
+ }
+
+ private void generateNext()
+ {
+
+ if (generatedBytes == 0)
+ {
+ // --- step 4 ---
+ prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.doFinal(a, 0);
+ }
+ else
+ {
+ // --- step 5a ---
+ prf.update(a, 0, a.length);
+ prf.doFinal(a, 0);
+ }
+
+ // --- step 5b ---
+ prf.update(a, 0, a.length);
+
+ if (useCounter)
+ {
+ int i = generatedBytes / h + 1;
+
+ // encode i into counter buffer
+ switch (ios.length)
+ {
+ case 4:
+ ios[0] = (byte)(i >>> 24);
+ // fall through
+ case 3:
+ ios[ios.length - 3] = (byte)(i >>> 16);
+ // fall through
+ case 2:
+ ios[ios.length - 2] = (byte)(i >>> 8);
+ // fall through
+ case 1:
+ ios[ios.length - 1] = (byte)i;
+ break;
+ default:
+ throw new IllegalStateException("Unsupported size of counter i");
+ }
+ prf.update(ios, 0, ios.length);
+ }
+
+ prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.doFinal(k, 0);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java
new file mode 100644
index 0000000..6003037
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java
@@ -0,0 +1,175 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.MacDerivationFunction;
+import org.bouncycastle.crypto.params.KDFFeedbackParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFFeedbackBytesGenerator
+ implements MacDerivationFunction
+{
+
+ private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ // please refer to the standard for the meaning of the variable names
+ // all field lengths are in bytes, not in bits as specified by the standard
+
+ // fields set by the constructor
+ private final Mac prf;
+ private final int h;
+
+ // fields set by init
+ private byte[] fixedInputData;
+ private int maxSizeExcl;
+ // ios is i defined as an octet string (the binary representation)
+ private byte[] ios;
+ private byte[] iv;
+ private boolean useCounter;
+
+ // operational
+ private int generatedBytes;
+ // k is used as buffer for all K(i) values
+ private byte[] k;
+
+
+ public KDFFeedbackBytesGenerator(Mac prf)
+ {
+ this.prf = prf;
+ this.h = prf.getMacSize();
+ this.k = new byte[h];
+ }
+
+ public void init(DerivationParameters params)
+ {
+ if (!(params instanceof KDFFeedbackParameters))
+ {
+ throw new IllegalArgumentException("Wrong type of arguments given");
+ }
+
+ KDFFeedbackParameters feedbackParams = (KDFFeedbackParameters)params;
+
+ // --- init mac based PRF ---
+
+ this.prf.init(new KeyParameter(feedbackParams.getKI()));
+
+ // --- set arguments ---
+
+ this.fixedInputData = feedbackParams.getFixedInputData();
+
+ int r = feedbackParams.getR();
+ this.ios = new byte[r / 8];
+
+ if (feedbackParams.useCounter())
+ {
+ // this is more conservative than the spec
+ BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+ this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+ Integer.MAX_VALUE : maxSize.intValue();
+ }
+ else
+ {
+ this.maxSizeExcl = Integer.MAX_VALUE;
+ }
+
+ this.iv = feedbackParams.getIV();
+ this.useCounter = feedbackParams.useCounter();
+
+ // --- set operational state ---
+
+ generatedBytes = 0;
+ }
+
+ public Mac getMac()
+ {
+ return prf;
+ }
+
+ public int generateBytes(byte[] out, int outOff, int len)
+ throws DataLengthException, IllegalArgumentException
+ {
+
+ int generatedBytesAfter = generatedBytes + len;
+ if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+ {
+ throw new DataLengthException(
+ "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+ }
+
+ if (generatedBytes % h == 0)
+ {
+ generateNext();
+ }
+
+ // copy what is left in the currentT (1..hash
+ int toGenerate = len;
+ int posInK = generatedBytes % h;
+ int leftInK = h - generatedBytes % h;
+ int toCopy = Math.min(leftInK, toGenerate);
+ System.arraycopy(k, posInK, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+
+ while (toGenerate > 0)
+ {
+ generateNext();
+ toCopy = Math.min(h, toGenerate);
+ System.arraycopy(k, 0, out, outOff, toCopy);
+ generatedBytes += toCopy;
+ toGenerate -= toCopy;
+ outOff += toCopy;
+ }
+
+ return len;
+ }
+
+ private void generateNext()
+ {
+
+ // TODO enable IV
+ if (generatedBytes == 0)
+ {
+ prf.update(iv, 0, iv.length);
+ }
+ else
+ {
+ prf.update(k, 0, k.length);
+ }
+
+ if (useCounter)
+ {
+ int i = generatedBytes / h + 1;
+
+ // encode i into counter buffer
+ switch (ios.length)
+ {
+ case 4:
+ ios[0] = (byte)(i >>> 24);
+ // fall through
+ case 3:
+ ios[ios.length - 3] = (byte)(i >>> 16);
+ // fall through
+ case 2:
+ ios[ios.length - 2] = (byte)(i >>> 8);
+ // fall through
+ case 1:
+ ios[ios.length - 1] = (byte)i;
+ break;
+ default:
+ throw new IllegalStateException("Unsupported size of counter i");
+ }
+ prf.update(ios, 0, ios.length);
+ }
+
+ prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.doFinal(k, 0);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
index 640ead4..0954d48 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
@@ -58,7 +58,7 @@ public class PKCS5S2ParametersGenerator
hMac.doFinal(state, 0);
System.arraycopy(state, 0, out, outOff, state.length);
-
+
for (int count = 1; count < c; count++)
{
hMac.update(state, 0, state.length);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java
new file mode 100644
index 0000000..9165973
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.macs.Poly1305;
+
+/**
+ * Generates keys for the Poly1305 MAC.
+ * <p>
+ * Poly1305 keys are 256 bit keys consisting of a 128 bit secret key used for the underlying block
+ * cipher followed by a 128 bit {@code r} value used for the polynomial portion of the Mac. <br>
+ * The {@code r} value has a specific format with some bits required to be cleared, resulting in an
+ * effective 106 bit key. <br>
+ * A separately generated 256 bit key can be modified to fit the Poly1305 key format by using the
+ * {@link #clamp(byte[])} method to clear the required bits.
+ *
+ * @see Poly1305
+ */
+public class Poly1305KeyGenerator
+ extends CipherKeyGenerator
+{
+ private static final byte R_MASK_LOW_2 = (byte)0xFC;
+ private static final byte R_MASK_HIGH_4 = (byte)0x0F;
+
+ /**
+ * Initialises the key generator.<br>
+ * Poly1305 keys are always 256 bits, so the key length in the provided parameters is ignored.
+ */
+ public void init(KeyGenerationParameters param)
+ {
+ // Poly1305 keys are always 256 bits
+ super.init(new KeyGenerationParameters(param.getRandom(), 256));
+ }
+
+ /**
+ * Generates a 256 bit key in the format required for Poly1305 - e.g.
+ * <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
+ * as per {@link #clamp(byte[])}.
+ */
+ public byte[] generateKey()
+ {
+ final byte[] key = super.generateKey();
+ clamp(key);
+ return key;
+ }
+
+ /**
+ * Modifies an existing 32 byte key value to comply with the requirements of the Poly1305 key by
+ * clearing required bits in the <code>r</code> (second 16 bytes) portion of the key.<br>
+ * Specifically:
+ * <ul>
+ * <li>r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})</li>
+ * <li>r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252})</li>
+ * </ul>
+ *
+ * @param a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code>
+ */
+ public static void clamp(byte[] key)
+ {
+ /*
+ * Key is k[0] ... k[15], r[0] ... r[15] as per poly1305_aes_clamp in ref impl.
+ */
+ if (key.length != 32)
+ {
+ throw new IllegalArgumentException("Poly1305 key must be 256 bits.");
+ }
+
+ /*
+ * r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})
+ */
+ key[19] &= R_MASK_HIGH_4;
+ key[23] &= R_MASK_HIGH_4;
+ key[27] &= R_MASK_HIGH_4;
+ key[31] &= R_MASK_HIGH_4;
+
+ /*
+ * r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252}).
+ */
+ key[20] &= R_MASK_LOW_2;
+ key[24] &= R_MASK_LOW_2;
+ key[28] &= R_MASK_LOW_2;
+ }
+
+ /**
+ * Checks a 32 byte key for compliance with the Poly1305 key requirements, e.g.
+ * <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
+ * as per {@link #clamp(byte[])}.
+ *
+ * @throws IllegalArgumentException if the key is of the wrong length, or has invalid bits set
+ * in the <code>r</code> portion of the key.
+ */
+ public static void checkKey(byte[] key)
+ {
+ if (key.length != 32)
+ {
+ throw new IllegalArgumentException("Poly1305 key must be 256 bits.");
+ }
+
+ checkMask(key[19], R_MASK_HIGH_4);
+ checkMask(key[23], R_MASK_HIGH_4);
+ checkMask(key[27], R_MASK_HIGH_4);
+ checkMask(key[31], R_MASK_HIGH_4);
+
+ checkMask(key[20], R_MASK_LOW_2);
+ checkMask(key[24], R_MASK_LOW_2);
+ checkMask(key[28], R_MASK_LOW_2);
+ }
+
+ private static void checkMask(byte b, byte mask)
+ {
+ if ((b & (~mask)) != 0)
+ {
+ throw new IllegalArgumentException("Invalid format for r portion of Poly1305 key.");
+ }
+ }
+
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html
deleted file mode 100644
index 9d73ce3..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Generators for keys, key pairs and password based encryption algorithms.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
index bb09a76..93b04e9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
@@ -5,15 +5,15 @@ import java.io.IOException;
import java.io.InputStream;
import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
/**
- * A CipherInputStream is composed of an InputStream and a BufferedBlockCipher so
- * that read() methods return data that are read in from the
- * underlying InputStream but have been additionally processed by the
- * Cipher. The Cipher must be fully initialized before being used by
- * a CipherInputStream.
- * <p>
+ * A CipherInputStream is composed of an InputStream and a cipher so that read() methods return data
+ * that are read in from the underlying InputStream but have been additionally processed by the
+ * Cipher. The cipher must be fully initialized before being used by a CipherInputStream.
+ * <p/>
* For example, if the Cipher is initialized for decryption, the
* CipherInputStream will attempt to read in data and decrypt them,
* before returning the decrypted data.
@@ -23,9 +23,10 @@ public class CipherInputStream
{
private BufferedBlockCipher bufferedBlockCipher;
private StreamCipher streamCipher;
+ private AEADBlockCipher aeadBlockCipher;
- private byte[] buf;
- private byte[] inBuf;
+ private final byte[] buf;
+ private final byte[] inBuf;
private int bufOff;
private int maxBuf;
@@ -62,95 +63,116 @@ public class CipherInputStream
}
/**
- * grab the next chunk of input from the underlying input stream
+ * Constructs a CipherInputStream from an InputStream and an AEADBlockCipher.
+ */
+ public CipherInputStream(InputStream is, AEADBlockCipher cipher)
+ {
+ super(is);
+
+ this.aeadBlockCipher = cipher;
+
+ buf = new byte[cipher.getOutputSize(INPUT_BUF_SIZE)];
+ inBuf = new byte[INPUT_BUF_SIZE];
+ }
+
+ /**
+ * Read data from underlying stream and process with cipher until end of stream or some data is
+ * available after cipher processing.
+ *
+ * @return -1 to indicate end of stream, or the number of bytes (> 0) available.
*/
private int nextChunk()
throws IOException
{
- int available = super.available();
-
- // must always try to read 1 byte!
- // some buggy InputStreams return < 0!
- if (available <= 0)
+ if (finalized)
{
- available = 1;
+ return -1;
}
- if (available > inBuf.length)
- {
- available = super.read(inBuf, 0, inBuf.length);
- }
- else
- {
- available = super.read(inBuf, 0, available);
- }
+ bufOff = 0;
+ maxBuf = 0;
- if (available < 0)
+ // Keep reading until EOF or cipher processing produces data
+ while (maxBuf == 0)
{
- if (finalized)
+ int read = in.read(inBuf);
+ if (read == -1)
{
- return -1;
+ finaliseCipher();
+ if (maxBuf == 0)
+ {
+ return -1;
+ }
+ return maxBuf;
}
try
{
if (bufferedBlockCipher != null)
{
- maxBuf = bufferedBlockCipher.doFinal(buf, 0);
+ maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, read, buf, 0);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ maxBuf = aeadBlockCipher.processBytes(inBuf, 0, read, buf, 0);
}
else
{
- maxBuf = 0; // a stream cipher
+ streamCipher.processBytes(inBuf, 0, read, buf, 0);
+ maxBuf = read;
}
}
catch (Exception e)
{
- throw new IOException("error processing stream: " + e.toString());
+ throw new IOException("Error processing stream " + e);
}
+ }
+ return maxBuf;
+ }
- bufOff = 0;
-
+ private void finaliseCipher()
+ throws IOException
+ {
+ try
+ {
finalized = true;
-
- if (bufOff == maxBuf)
+ if (bufferedBlockCipher != null)
{
- return -1;
+ maxBuf = bufferedBlockCipher.doFinal(buf, 0);
}
- }
- else
- {
- bufOff = 0;
-
- try
+ else if (aeadBlockCipher != null)
{
- if (bufferedBlockCipher != null)
- {
- maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, available, buf, 0);
- }
- else
- {
- streamCipher.processBytes(inBuf, 0, available, buf, 0);
- maxBuf = available;
- }
+ maxBuf = aeadBlockCipher.doFinal(buf, 0);
}
- catch (Exception e)
+ else
{
- throw new IOException("error processing stream: " + e.toString());
- }
-
- if (maxBuf == 0) // not enough bytes read for first block...
- {
- return nextChunk();
+ maxBuf = 0; // a stream cipher
}
}
-
- return maxBuf;
+ catch (final InvalidCipherTextException e)
+ {
+ throw new InvalidCipherTextIOException("Error finalising cipher", e);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Error finalising cipher " + e);
+ }
}
+ /**
+ * Reads data from the underlying stream and processes it with the cipher until the cipher
+ * outputs data, and returns the next available byte.
+ * <p/>
+ * If the underlying stream is exhausted by this call, the cipher will be finalised.
+ *
+ * @throws IOException if there was an error closing the input stream.
+ * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
public int read()
throws IOException
{
- if (bufOff == maxBuf)
+ if (bufOff >= maxBuf)
{
if (nextChunk() < 0)
{
@@ -161,6 +183,19 @@ public class CipherInputStream
return buf[bufOff++] & 0xff;
}
+ /**
+ * Reads data from the underlying stream and processes it with the cipher until the cipher
+ * outputs data, and then returns up to <code>b.length</code> bytes in the provided array.
+ * <p/>
+ * If the underlying stream is exhausted by this call, the cipher will be finalised.
+ *
+ * @param b the buffer into which the data is 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 there was an error closing the input stream.
+ * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
public int read(
byte[] b)
throws IOException
@@ -168,13 +203,28 @@ public class CipherInputStream
return read(b, 0, b.length);
}
+ /**
+ * Reads data from the underlying stream and processes it with the cipher until the cipher
+ * outputs data, and then returns up to <code>len</code> bytes in the provided array.
+ * <p/>
+ * If the underlying stream is exhausted by this call, the cipher will be finalised.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off the start offset in the destination array <code>b</code>
+ * @param len the maximum number of bytes 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 there was an error closing the input stream.
+ * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
public int read(
byte[] b,
int off,
int len)
throws IOException
{
- if (bufOff == maxBuf)
+ if (bufOff >= maxBuf)
{
if (nextChunk() < 0)
{
@@ -182,22 +232,10 @@ public class CipherInputStream
}
}
- int available = maxBuf - bufOff;
-
- if (len > available)
- {
- System.arraycopy(buf, bufOff, b, off, available);
- bufOff = maxBuf;
-
- return available;
- }
- else
- {
- System.arraycopy(buf, bufOff, b, off, len);
- bufOff += len;
-
- return len;
- }
+ int toSupply = Math.min(len, available());
+ System.arraycopy(buf, bufOff, b, off, toSupply);
+ bufOff += toSupply;
+ return toSupply;
}
public long skip(
@@ -209,36 +247,55 @@ public class CipherInputStream
return 0;
}
- int available = maxBuf - bufOff;
+ int skip = (int)Math.min(n, available());
+ bufOff += skip;
+ return skip;
+ }
- if (n > available)
- {
- bufOff = maxBuf;
+ public int available()
+ throws IOException
+ {
+ return maxBuf - bufOff;
+ }
- return available;
+ /**
+ * Closes the underlying input stream and finalises the processing of the data by the cipher.
+ *
+ * @throws IOException if there was an error closing the input stream.
+ * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+ */
+ public void close()
+ throws IOException
+ {
+ try
+ {
+ in.close();
}
- else
+ finally
{
- bufOff += (int)n;
-
- return (int)n;
+ if (!finalized)
+ {
+ // Reset the cipher, discarding any data buffered in it
+ // Errors in cipher finalisation trump I/O error closing input
+ finaliseCipher();
+ }
}
+ maxBuf = bufOff = 0;
}
- public int available()
- throws IOException
+ public void mark(int readlimit)
{
- return maxBuf - bufOff;
}
- public void close()
+ public void reset()
throws IOException
{
- super.close();
}
public boolean markSupported()
{
return false;
}
+
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
index 17a7b6d..9beb5b9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
@@ -5,15 +5,27 @@ import java.io.IOException;
import java.io.OutputStream;
import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+/**
+ * A CipherOutputStream is composed of an OutputStream and a cipher so that write() methods process
+ * the written data with the cipher, and the output of the cipher is in turn written to the
+ * underlying OutputStream. The cipher must be fully initialized before being used by a
+ * CipherInputStream.
+ * <p/>
+ * For example, if the cipher is initialized for encryption, the CipherOutputStream will encrypt the
+ * data before writing the encrypted data to the underlying stream.
+ */
public class CipherOutputStream
extends FilterOutputStream
{
private BufferedBlockCipher bufferedBlockCipher;
private StreamCipher streamCipher;
+ private AEADBlockCipher aeadBlockCipher;
- private byte[] oneByte = new byte[1];
+ private final byte[] oneByte = new byte[1];
private byte[] buf;
/**
@@ -26,7 +38,6 @@ public class CipherOutputStream
{
super(os);
this.bufferedBlockCipher = cipher;
- this.buf = new byte[cipher.getBlockSize()];
}
/**
@@ -42,10 +53,19 @@ public class CipherOutputStream
}
/**
+ * Constructs a CipherOutputStream from an OutputStream and a AEADBlockCipher.
+ */
+ public CipherOutputStream(OutputStream os, AEADBlockCipher cipher)
+ {
+ super(os);
+ this.aeadBlockCipher = cipher;
+ }
+
+ /**
* Writes the specified byte to this output stream.
*
* @param b the <code>byte</code>.
- * @exception java.io.IOException if an I/O error occurs.
+ * @throws java.io.IOException if an I/O error occurs.
*/
public void write(
int b)
@@ -53,32 +73,27 @@ public class CipherOutputStream
{
oneByte[0] = (byte)b;
- if (bufferedBlockCipher != null)
+ if (streamCipher != null)
{
- int len = bufferedBlockCipher.processBytes(oneByte, 0, 1, buf, 0);
-
- if (len != 0)
- {
- out.write(buf, 0, len);
- }
+ out.write(streamCipher.returnByte((byte)b));
}
else
{
- out.write(streamCipher.returnByte((byte)b));
+ write(oneByte, 0, 1);
}
}
/**
* Writes <code>b.length</code> bytes from the specified byte array
* to this output stream.
- * <p>
+ * <p/>
* The <code>write</code> method of
* <code>CipherOutputStream</code> calls the <code>write</code>
* method of three arguments with the three arguments
* <code>b</code>, <code>0</code>, and <code>b.length</code>.
*
* @param b the data.
- * @exception java.io.IOException if an I/O error occurs.
+ * @throws java.io.IOException if an I/O error occurs.
* @see #write(byte[], int, int)
*/
public void write(
@@ -92,10 +107,10 @@ public class CipherOutputStream
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this output stream.
*
- * @param b the data.
+ * @param b the data.
* @param off the start offset in the data.
* @param len the number of bytes to write.
- * @exception java.io.IOException if an I/O error occurs.
+ * @throws java.io.IOException if an I/O error occurs.
*/
public void write(
byte[] b,
@@ -103,10 +118,10 @@ public class CipherOutputStream
int len)
throws IOException
{
+ ensureCapacity(len);
+
if (bufferedBlockCipher != null)
{
- byte[] buf = new byte[bufferedBlockCipher.getOutputSize(len)];
-
int outLen = bufferedBlockCipher.processBytes(b, off, len, buf, 0);
if (outLen != 0)
@@ -114,10 +129,17 @@ public class CipherOutputStream
out.write(buf, 0, outLen);
}
}
- else
+ else if (aeadBlockCipher != null)
{
- byte[] buf = new byte[len];
+ int outLen = aeadBlockCipher.processBytes(b, off, len, buf, 0);
+ if (outLen != 0)
+ {
+ out.write(buf, 0, outLen);
+ }
+ }
+ else
+ {
streamCipher.processBytes(b, off, len, buf, 0);
out.write(buf, 0, len);
@@ -125,49 +147,78 @@ public class CipherOutputStream
}
/**
+ * Ensure the ciphertext buffer has space sufficient to accept an upcoming output.
+ *
+ * @param outputSize the size of the pending update.
+ */
+ private void ensureCapacity(int outputSize)
+ {
+ // This overestimates buffer on updates for AEAD/padded, but keeps it simple.
+ int bufLen;
+ if (bufferedBlockCipher != null)
+ {
+ bufLen = bufferedBlockCipher.getOutputSize(outputSize);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ bufLen = aeadBlockCipher.getOutputSize(outputSize);
+ }
+ else
+ {
+ bufLen = outputSize;
+ }
+ if ((buf == null) || (buf.length < bufLen))
+ {
+ buf = new byte[bufLen];
+ }
+ }
+
+ /**
* Flushes this output stream by forcing any buffered output bytes
* that have already been processed by the encapsulated cipher object
* to be written out.
- *
- * <p>
+ * <p/>
+ * <p/>
* Any bytes buffered by the encapsulated cipher
* and waiting to be processed by it will not be written out. For example,
* if the encapsulated cipher is a block cipher, and the total number of
* bytes written using one of the <code>write</code> methods is less than
* the cipher's block size, no bytes will be written out.
*
- * @exception java.io.IOException if an I/O error occurs.
+ * @throws java.io.IOException if an I/O error occurs.
*/
public void flush()
throws IOException
{
- super.flush();
+ out.flush();
}
/**
* Closes this output stream and releases any system resources
* associated with this stream.
- * <p>
+ * <p/>
* This method invokes the <code>doFinal</code> method of the encapsulated
* cipher object, which causes any bytes buffered by the encapsulated
* cipher to be processed. The result is written out by calling the
* <code>flush</code> method of this output stream.
- * <p>
+ * <p/>
* This method resets the encapsulated cipher object to its initial state
* and calls the <code>close</code> method of the underlying output
* stream.
*
- * @exception java.io.IOException if an I/O error occurs.
+ * @throws java.io.IOException if an I/O error occurs.
+ * @throws InvalidCipherTextIOException if the data written to this stream was invalid ciphertext
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
*/
public void close()
throws IOException
{
+ ensureCapacity(0);
+ IOException error = null;
try
{
if (bufferedBlockCipher != null)
{
- byte[] buf = new byte[bufferedBlockCipher.getOutputSize(0)];
-
int outLen = bufferedBlockCipher.doFinal(buf, 0);
if (outLen != 0)
@@ -175,14 +226,41 @@ public class CipherOutputStream
out.write(buf, 0, outLen);
}
}
+ else if (aeadBlockCipher != null)
+ {
+ int outLen = aeadBlockCipher.doFinal(buf, 0);
+
+ if (outLen != 0)
+ {
+ out.write(buf, 0, outLen);
+ }
+ }
+ }
+ catch (final InvalidCipherTextException e)
+ {
+ error = new InvalidCipherTextIOException("Error finalising cipher data", e);
}
catch (Exception e)
{
- throw new IOException("Error closing stream: " + e.toString());
+ error = new IOException("Error closing stream: " + e);
}
- flush();
-
- super.close();
+ try
+ {
+ flush();
+ out.close();
+ }
+ catch (IOException e)
+ {
+ // Invalid ciphertext takes precedence over close error
+ if (error == null)
+ {
+ error = e;
+ }
+ }
+ if (error != null)
+ {
+ throw error;
+ }
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java
new file mode 100644
index 0000000..b601d4c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.IOException;
+
+/**
+ * {@link IOException} wrapper around an exception indicating an invalid ciphertext, such as in
+ * authentication failure during finalisation of an AEAD cipher. For use in streams that need to
+ * expose invalid ciphertext errors.
+ */
+public class InvalidCipherTextIOException
+ extends IOException
+{
+ private static final long serialVersionUID = 1L;
+
+ private final Throwable cause;
+
+ public InvalidCipherTextIOException(String message, Throwable cause)
+ {
+ super(message);
+
+ this.cause = cause;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html
deleted file mode 100644
index f2c9e40..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for doing "enhanced" I/O with Digests and MACs.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
index f4dfc6e..82ac20c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
@@ -6,11 +6,13 @@ import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DerivationFunction;
import org.bouncycastle.crypto.KeyEncapsulation;
+import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.KDFParameters;
import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.BigIntegers;
@@ -106,33 +108,34 @@ public class ECIESKeyEncapsulation
throw new IllegalArgumentException("Public key required for encryption");
}
- BigInteger n = key.getParameters().getN();
- BigInteger h = key.getParameters().getH();
+ ECPublicKeyParameters ecPubKey = (ECPublicKeyParameters)key;
+ ECDomainParameters ecParams = ecPubKey.getParameters();
+ ECCurve curve = ecParams.getCurve();
+ BigInteger n = ecParams.getN();
+ BigInteger h = ecParams.getH();
// Generate the ephemeral key pair
BigInteger r = BigIntegers.createRandomInRange(ONE, n, rnd);
- ECPoint gTilde = key.getParameters().getG().multiply(r);
+
+ // Compute the static-ephemeral key agreement
+ BigInteger rPrime = CofactorMode ? r.multiply(h).mod(n) : r;
+
+ ECPoint[] ghTilde = new ECPoint[]{
+ ecParams.getG().multiply(r),
+ ecPubKey.getQ().multiply(rPrime)
+ };
+
+ // NOTE: More efficient than normalizing each individually
+ curve.normalizeAll(ghTilde);
+
+ ECPoint gTilde = ghTilde[0], hTilde = ghTilde[1];
// Encode the ephemeral public key
byte[] C = gTilde.getEncoded();
System.arraycopy(C, 0, out, outOff, C.length);
- // Compute the static-ephemeral key agreement
- BigInteger rPrime;
- if (CofactorMode)
- {
- rPrime = r.multiply(h).mod(n);
- }
- else
- {
- rPrime = r;
- }
-
- ECPoint hTilde = ((ECPublicKeyParameters)key).getQ().multiply(rPrime);
-
// Encode the shared secret value
- int PEHlen = (key.getParameters().getCurve().getFieldSize() + 7) / 8;
- byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger());
+ byte[] PEH = hTilde.getAffineXCoord().getEncoded();
// Initialise the KDF
byte[] kdfInput;
@@ -186,40 +189,36 @@ public class ECIESKeyEncapsulation
throw new IllegalArgumentException("Private key required for encryption");
}
- BigInteger n = key.getParameters().getN();
- BigInteger h = key.getParameters().getH();
+ ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters)key;
+ ECDomainParameters ecParams = ecPrivKey.getParameters();
+ ECCurve curve = ecParams.getCurve();
+ BigInteger n = ecParams.getN();
+ BigInteger h = ecParams.getH();
// Decode the ephemeral public key
byte[] C = new byte[inLen];
System.arraycopy(in, inOff, C, 0, inLen);
- ECPoint gTilde = key.getParameters().getCurve().decodePoint(C);
+
+ // NOTE: Decoded points are already normalized (i.e in affine form)
+ ECPoint gTilde = curve.decodePoint(C);
// Compute the static-ephemeral key agreement
- ECPoint gHat;
+ ECPoint gHat = gTilde;
if ((CofactorMode) || (OldCofactorMode))
{
- gHat = gTilde.multiply(h);
- }
- else
- {
- gHat = gTilde;
+ gHat = gHat.multiply(h);
}
- BigInteger xHat;
+ BigInteger xHat = ecPrivKey.getD();
if (CofactorMode)
{
- xHat = ((ECPrivateKeyParameters)key).getD().multiply(h.modInverse(n)).mod(n);
- }
- else
- {
- xHat = ((ECPrivateKeyParameters)key).getD();
+ xHat = xHat.multiply(h.modInverse(n)).mod(n);
}
- ECPoint hTilde = gHat.multiply(xHat);
+ ECPoint hTilde = gHat.multiply(xHat).normalize();
// Encode the shared secret value
- int PEHlen = (key.getParameters().getCurve().getFieldSize() + 7) / 8;
- byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger());
+ byte[] PEH = hTilde.getAffineXCoord().getEncoded();
// Initialise the KDF
byte[] kdfInput;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html
deleted file mode 100644
index a5174b3..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-The Key Encapsulation Mechanisms (KEMs) from ISO 18033-2.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java
index 8a3b5bb..1aa8ede 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java
@@ -5,6 +5,7 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+import org.bouncycastle.crypto.params.KeyParameter;
/**
* CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
@@ -103,25 +104,36 @@ public class CMac implements Mac
return cipher.getAlgorithmName();
}
- private static byte[] doubleLu(byte[] in)
+ private static int shiftLeft(byte[] block, byte[] output)
{
- int FirstBit = (in[0] & 0xFF) >> 7;
- byte[] ret = new byte[in.length];
- for (int i = 0; i < in.length - 1; i++)
- {
- ret[i] = (byte)((in[i] << 1) + ((in[i + 1] & 0xFF) >> 7));
- }
- ret[in.length - 1] = (byte)(in[in.length - 1] << 1);
- if (FirstBit == 1)
+ int i = block.length;
+ int bit = 0;
+ while (--i >= 0)
{
- ret[in.length - 1] ^= in.length == 16 ? CONSTANT_128 : CONSTANT_64;
+ int b = block[i] & 0xff;
+ output[i] = (byte)((b << 1) | bit);
+ bit = (b >>> 7) & 1;
}
+ return bit;
+ }
+
+ private static byte[] doubleLu(byte[] in)
+ {
+ byte[] ret = new byte[in.length];
+ int carry = shiftLeft(in, ret);
+ int xor = 0xff & (in.length == 16 ? CONSTANT_128 : CONSTANT_64);
+
+ /*
+ * NOTE: This construction is an attempt at a constant-time implementation.
+ */
+ ret[in.length - 1] ^= (xor >>> ((1 - carry) << 3));
+
return ret;
}
public void init(CipherParameters params)
{
- if (params != null)
+ if (params instanceof KeyParameter)
{
cipher.init(true, params);
@@ -130,6 +142,10 @@ public class CMac implements Mac
cipher.processBlock(ZEROES, 0, L, 0);
Lu = doubleLu(L);
Lu2 = doubleLu(Lu);
+ } else if (params != null)
+ {
+ // CMAC mode does not permit IV to underlying CBC mode
+ throw new IllegalArgumentException("CMac mode only permits key to be set.");
}
reset();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
new file mode 100644
index 0000000..150eb61
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
@@ -0,0 +1,279 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.util.Pack;
+
+/**
+ * Poly1305 message authentication code, designed by D. J. Bernstein.
+ * <p>
+ * Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
+ * consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
+ * effective key bits) used in the authenticator.
+ * <p>
+ * The polynomial calculation in this implementation is adapted from the public domain <a
+ * href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
+ * by Andrew M (@floodyberry).
+ * @see Poly1305KeyGenerator
+ */
+public class Poly1305
+ implements Mac
+{
+ private static final int BLOCK_SIZE = 16;
+
+ private final BlockCipher cipher;
+
+ private final byte[] singleByte = new byte[1];
+
+ // Initialised state
+
+ /** Polynomial key */
+ private int r0, r1, r2, r3, r4;
+
+ /** Precomputed 5 * r[1..4] */
+ private int s1, s2, s3, s4;
+
+ /** Encrypted nonce */
+ private int k0, k1, k2, k3;
+
+ // Accumulating state
+
+ /** Current block of buffered input */
+ private final byte[] currentBlock = new byte[BLOCK_SIZE];
+
+ /** Current offset in input buffer */
+ private int currentBlockOffset = 0;
+
+ /** Polynomial accumulator */
+ private int h0, h1, h2, h3, h4;
+
+ /**
+ * Constructs a Poly1305 MAC, using a 128 bit block cipher.
+ */
+ public Poly1305(final BlockCipher cipher)
+ {
+ if (cipher.getBlockSize() != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("Poly1305 requires a 128 bit block cipher.");
+ }
+ this.cipher = cipher;
+ }
+
+ /**
+ * Initialises the Poly1305 MAC.
+ *
+ * @param a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
+ * a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.
+ */
+ public void init(final CipherParameters params)
+ throws IllegalArgumentException
+ {
+ final byte[] nonce;
+ final byte[] key;
+ if ((params instanceof ParametersWithIV) && ((ParametersWithIV)params).getParameters() instanceof KeyParameter)
+ {
+ nonce = ((ParametersWithIV)params).getIV();
+ key = ((KeyParameter)((ParametersWithIV)params).getParameters()).getKey();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Poly1305 requires a key and and IV.");
+ }
+
+ setKey(key, nonce);
+ reset();
+ }
+
+ private void setKey(final byte[] key, final byte[] nonce)
+ {
+ if (nonce.length != BLOCK_SIZE)
+ {
+ throw new IllegalArgumentException("Poly1305 requires a 128 bit IV.");
+ }
+ Poly1305KeyGenerator.checkKey(key);
+
+ // Extract r portion of key
+ int t0 = Pack.littleEndianToInt(key, BLOCK_SIZE + 0);
+ int t1 = Pack.littleEndianToInt(key, BLOCK_SIZE + 4);
+ int t2 = Pack.littleEndianToInt(key, BLOCK_SIZE + 8);
+ int t3 = Pack.littleEndianToInt(key, BLOCK_SIZE + 12);
+
+ r0 = t0 & 0x3ffffff; t0 >>>= 26; t0 |= t1 << 6;
+ r1 = t0 & 0x3ffff03; t1 >>>= 20; t1 |= t2 << 12;
+ r2 = t1 & 0x3ffc0ff; t2 >>>= 14; t2 |= t3 << 18;
+ r3 = t2 & 0x3f03fff; t3 >>>= 8;
+ r4 = t3 & 0x00fffff;
+
+ // Precompute multipliers
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ // Compute encrypted nonce
+ final byte[] cipherKey = new byte[BLOCK_SIZE];
+ System.arraycopy(key, 0, cipherKey, 0, cipherKey.length);
+
+ cipher.init(true, new KeyParameter(cipherKey));
+ cipher.processBlock(nonce, 0, cipherKey, 0);
+
+ k0 = Pack.littleEndianToInt(cipherKey, 0);
+ k1 = Pack.littleEndianToInt(cipherKey, 4);
+ k2 = Pack.littleEndianToInt(cipherKey, 8);
+ k3 = Pack.littleEndianToInt(cipherKey, 12);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Poly1305-" + cipher.getAlgorithmName();
+ }
+
+ public int getMacSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void update(final byte in)
+ throws IllegalStateException
+ {
+ singleByte[0] = in;
+ update(singleByte, 0, 1);
+ }
+
+ public void update(final byte[] in, final int inOff, final int len)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ processBlock();
+ currentBlockOffset = 0;
+ }
+
+ int toCopy = Math.min((len - copied), BLOCK_SIZE - currentBlockOffset);
+ System.arraycopy(in, copied + inOff, currentBlock, currentBlockOffset, toCopy);
+ copied += toCopy;
+ currentBlockOffset += toCopy;
+ }
+
+ }
+
+ private void processBlock()
+ {
+ if (currentBlockOffset < BLOCK_SIZE)
+ {
+ currentBlock[currentBlockOffset] = 1;
+ for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++)
+ {
+ currentBlock[i] = 0;
+ }
+ }
+
+ final long t0 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 0);
+ final long t1 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 4);
+ final long t2 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 8);
+ final long t3 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 12);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += (((t1 << 32) | t0) >>> 26) & 0x3ffffff;
+ h2 += (((t2 << 32) | t1) >>> 20) & 0x3ffffff;
+ h3 += (((t3 << 32) | t2) >>> 14) & 0x3ffffff;
+ h4 += (t3 >>> 8);
+
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ h4 += (1 << 24);
+ }
+
+ long tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+ long tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+ long tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+ long tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+ long tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+ long b;
+ h0 = (int)tp0 & 0x3ffffff; b = (tp0 >>> 26);
+ tp1 += b; h1 = (int)tp1 & 0x3ffffff; b = ((tp1 >>> 26) & 0xffffffff);
+ tp2 += b; h2 = (int)tp2 & 0x3ffffff; b = ((tp2 >>> 26) & 0xffffffff);
+ tp3 += b; h3 = (int)tp3 & 0x3ffffff; b = (tp3 >>> 26);
+ tp4 += b; h4 = (int)tp4 & 0x3ffffff; b = (tp4 >>> 26);
+ h0 += b * 5;
+ }
+
+ public int doFinal(final byte[] out, final int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ if (outOff + BLOCK_SIZE > out.length)
+ {
+ throw new DataLengthException("Output buffer is too short.");
+ }
+
+ if (currentBlockOffset > 0)
+ {
+ // Process padded final block
+ processBlock();
+ }
+
+ long f0, f1, f2, f3;
+
+ int b = h0 >>> 26;
+ h0 = h0 & 0x3ffffff;
+ h1 += b; b = h1 >>> 26; h1 = h1 & 0x3ffffff;
+ h2 += b; b = h2 >>> 26; h2 = h2 & 0x3ffffff;
+ h3 += b; b = h3 >>> 26; h3 = h3 & 0x3ffffff;
+ h4 += b; b = h4 >>> 26; h4 = h4 & 0x3ffffff;
+ h0 += b * 5;
+
+ int g0, g1, g2, g3, g4;
+ g0 = h0 + 5; b = g0 >>> 26; g0 &= 0x3ffffff;
+ g1 = h1 + b; b = g1 >>> 26; g1 &= 0x3ffffff;
+ g2 = h2 + b; b = g2 >>> 26; g2 &= 0x3ffffff;
+ g3 = h3 + b; b = g3 >>> 26; g3 &= 0x3ffffff;
+ g4 = h4 + b - (1 << 26);
+
+ b = (g4 >>> 31) - 1;
+ int nb = ~b;
+ h0 = (h0 & nb) | (g0 & b);
+ h1 = (h1 & nb) | (g1 & b);
+ h2 = (h2 & nb) | (g2 & b);
+ h3 = (h3 & nb) | (g3 & b);
+ h4 = (h4 & nb) | (g4 & b);
+
+ f0 = (((h0 ) | (h1 << 26)) & 0xffffffffl) + (0xffffffffL & k0);
+ f1 = (((h1 >>> 6 ) | (h2 << 20)) & 0xffffffffl) + (0xffffffffL & k1);
+ f2 = (((h2 >>> 12) | (h3 << 14)) & 0xffffffffl) + (0xffffffffL & k2);
+ f3 = (((h3 >>> 18) | (h4 << 8 )) & 0xffffffffl) + (0xffffffffL & k3);
+
+ Pack.intToLittleEndian((int)f0, out, outOff);
+ f1 += (f0 >>> 32);
+ Pack.intToLittleEndian((int)f1, out, outOff + 4);
+ f2 += (f1 >>> 32);
+ Pack.intToLittleEndian((int)f2, out, outOff + 8);
+ f3 += (f2 >>> 32);
+ Pack.intToLittleEndian((int)f3, out, outOff + 12);
+
+ reset();
+ return BLOCK_SIZE;
+ }
+
+ public void reset()
+ {
+ currentBlockOffset = 0;
+
+ h0 = h1 = h2 = h3 = h4 = 0;
+ }
+
+ private static final long mul32x32_64(int i1, int i2)
+ {
+ return ((long)i1) * i2;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
new file mode 100644
index 0000000..6097247
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.SkeinEngine;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.SkeinParameters;
+
+/**
+ * Implementation of the Skein parameterised MAC function in 256, 512 and 1024 bit block sizes,
+ * based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p/>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p/>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p/>
+ *
+ * @see SkeinEngine
+ * @see SkeinParameters
+ */
+public class SkeinMac
+ implements Mac
+{
+ /**
+ * 256 bit block size - Skein MAC-256
+ */
+ public static final int SKEIN_256 = SkeinEngine.SKEIN_256;
+ /**
+ * 512 bit block size - Skein MAC-512
+ */
+ public static final int SKEIN_512 = SkeinEngine.SKEIN_512;
+ /**
+ * 1024 bit block size - Skein MAC-1024
+ */
+ public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024;
+
+ private SkeinEngine engine;
+
+ /**
+ * Constructs a Skein MAC with an internal state size and output size.
+ *
+ * @param stateSizeBits the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+ * {@link #SKEIN_1024}.
+ * @param digestSizeBits the output/MAC size to produce in bits, which must be an integral number of bytes.
+ */
+ public SkeinMac(int stateSizeBits, int digestSizeBits)
+ {
+ this.engine = new SkeinEngine(stateSizeBits, digestSizeBits);
+ }
+
+ public SkeinMac(SkeinMac mac)
+ {
+ this.engine = new SkeinEngine(mac.engine);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Skein-MAC-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8);
+ }
+
+ /**
+ * Initialises the Skein digest with the provided parameters.<br>
+ * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function.
+ *
+ * @param params an instance of {@link SkeinParameters} or {@link KeyParameter}.
+ */
+ public void init(CipherParameters params)
+ throws IllegalArgumentException
+ {
+ SkeinParameters skeinParameters;
+ if (params instanceof SkeinParameters)
+ {
+ skeinParameters = (SkeinParameters)params;
+ }
+ else if (params instanceof KeyParameter)
+ {
+ skeinParameters = new SkeinParameters.Builder().setKey(((KeyParameter)params).getKey()).build();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid parameter passed to Skein MAC init - "
+ + params.getClass().getName());
+ }
+ if (skeinParameters.getKey() == null)
+ {
+ throw new IllegalArgumentException("Skein MAC requires a key parameter.");
+ }
+ engine.init(skeinParameters);
+ }
+
+ public int getMacSize()
+ {
+ return engine.getOutputSize();
+ }
+
+ public void reset()
+ {
+ engine.reset();
+ }
+
+ public void update(byte in)
+ {
+ engine.update(in);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ engine.update(in, inOff, len);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ return engine.doFinal(out, outOff);
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html
deleted file mode 100644
index 0b1f86d..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for creating MACs and HMACs.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
index 9a6e2e0..fef51fd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -29,8 +29,8 @@ public class CCMBlockCipher
private int macSize;
private CipherParameters keyParam;
private byte[] macBlock;
- private ByteArrayOutputStream associatedText = new ByteArrayOutputStream();
- private ByteArrayOutputStream data = new ByteArrayOutputStream();
+ private ExposedByteArrayOutputStream associatedText = new ExposedByteArrayOutputStream();
+ private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
/**
* Basic constructor.
@@ -65,6 +65,7 @@ public class CCMBlockCipher
{
this.forEncryption = forEncryption;
+ CipherParameters cipherParameters;
if (params instanceof AEADParameters)
{
AEADParameters param = (AEADParameters)params;
@@ -72,7 +73,7 @@ public class CCMBlockCipher
nonce = param.getNonce();
initialAssociatedText = param.getAssociatedText();
macSize = param.getMacSize() / 8;
- keyParam = param.getKey();
+ cipherParameters = param.getKey();
}
else if (params instanceof ParametersWithIV)
{
@@ -81,17 +82,25 @@ public class CCMBlockCipher
nonce = param.getIV();
initialAssociatedText = null;
macSize = macBlock.length / 2;
- keyParam = param.getParameters();
+ cipherParameters = param.getParameters();
}
else
{
throw new IllegalArgumentException("invalid parameters passed to CCM");
}
+ // NOTE: Very basic support for key re-use, but no performance gain from it
+ if (cipherParameters != null)
+ {
+ keyParam = cipherParameters;
+ }
+
if (nonce == null || nonce.length < 7 || nonce.length > 13)
{
throw new IllegalArgumentException("nonce must have length from 7 to 13 octets");
}
+
+ reset();
}
public String getAlgorithmName()
@@ -129,14 +138,11 @@ public class CCMBlockCipher
public int doFinal(byte[] out, int outOff)
throws IllegalStateException, InvalidCipherTextException
{
- byte[] text = data.toByteArray();
- byte[] enc = processPacket(text, 0, text.length);
-
- System.arraycopy(enc, 0, out, outOff, enc.length);
+ int len = processPacket(data.getBuffer(), 0, data.size(), out, outOff);
reset();
- return enc.length;
+ return len;
}
public void reset()
@@ -178,9 +184,55 @@ public class CCMBlockCipher
return totalData < macSize ? 0 : totalData - macSize;
}
+ /**
+ * Process a packet of data for either CCM decryption or encryption.
+ *
+ * @param in data for processing.
+ * @param inOff offset at which data starts in the input array.
+ * @param inLen length of the data in the input array.
+ * @return a byte array containing the processed input..
+ * @throws IllegalStateException if the cipher is not appropriately set up.
+ * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
+ */
public byte[] processPacket(byte[] in, int inOff, int inLen)
throws IllegalStateException, InvalidCipherTextException
{
+ byte[] output;
+
+ if (forEncryption)
+ {
+ output = new byte[inLen + macSize];
+ }
+ else
+ {
+ if (inLen < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ output = new byte[inLen - macSize];
+ }
+
+ processPacket(in, inOff, inLen, output, 0);
+
+ return output;
+ }
+
+ /**
+ * Process a packet of data for either CCM decryption or encryption.
+ *
+ * @param in data for processing.
+ * @param inOff offset at which data starts in the input array.
+ * @param inLen length of the data in the input array.
+ * @param output output array.
+ * @param outOff offset into output array to start putting processed bytes.
+ * @return the number of bytes added to output.
+ * @throws IllegalStateException if the cipher is not appropriately set up.
+ * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
+ * @throws DataLengthException if output buffer too short.
+ */
+ public int processPacket(byte[] in, int inOff, int inLen, byte[] output, int outOff)
+ throws IllegalStateException, InvalidCipherTextException, DataLengthException
+ {
// TODO: handle null keyParam (e.g. via RepeatedKeySpec)
// Need to keep the CTR and CBC Mac parts around and reset
if (keyParam == null)
@@ -206,42 +258,52 @@ public class CCMBlockCipher
BlockCipher ctrCipher = new SICBlockCipher(cipher);
ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
- int index = inOff;
- int outOff = 0;
- byte[] output;
+ int outputLen;
+ int inIndex = inOff;
+ int outIndex = outOff;
if (forEncryption)
{
- output = new byte[inLen + macSize];
+ outputLen = inLen + macSize;
+ if (output.length < (outputLen + outOff))
+ {
+ throw new DataLengthException("Output buffer too short.");
+ }
calculateMac(in, inOff, inLen, macBlock);
ctrCipher.processBlock(macBlock, 0, macBlock, 0); // S0
- while (index < inLen - blockSize) // S1...
+ while (inIndex < (inOff + inLen - blockSize)) // S1...
{
- ctrCipher.processBlock(in, index, output, outOff);
- outOff += blockSize;
- index += blockSize;
+ ctrCipher.processBlock(in, inIndex, output, outIndex);
+ outIndex += blockSize;
+ inIndex += blockSize;
}
byte[] block = new byte[blockSize];
- System.arraycopy(in, index, block, 0, inLen - index);
+ System.arraycopy(in, inIndex, block, 0, inLen + inOff - inIndex);
ctrCipher.processBlock(block, 0, block, 0);
- System.arraycopy(block, 0, output, outOff, inLen - index);
+ System.arraycopy(block, 0, output, outIndex, inLen + inOff - inIndex);
- outOff += inLen - index;
-
- System.arraycopy(macBlock, 0, output, outOff, output.length - outOff);
+ System.arraycopy(macBlock, 0, output, outOff + inLen, macSize);
}
else
{
- output = new byte[inLen - macSize];
+ if (inLen < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
+ outputLen = inLen - macSize;
+ if (output.length < (outputLen + outOff))
+ {
+ throw new DataLengthException("Output buffer too short.");
+ }
- System.arraycopy(in, inOff + inLen - macSize, macBlock, 0, macSize);
+ System.arraycopy(in, inOff + outputLen, macBlock, 0, macSize);
ctrCipher.processBlock(macBlock, 0, macBlock, 0);
@@ -250,24 +312,24 @@ public class CCMBlockCipher
macBlock[i] = 0;
}
- while (outOff < output.length - blockSize)
+ while (inIndex < (inOff + outputLen - blockSize))
{
- ctrCipher.processBlock(in, index, output, outOff);
- outOff += blockSize;
- index += blockSize;
+ ctrCipher.processBlock(in, inIndex, output, outIndex);
+ outIndex += blockSize;
+ inIndex += blockSize;
}
byte[] block = new byte[blockSize];
- System.arraycopy(in, index, block, 0, output.length - outOff);
+ System.arraycopy(in, inIndex, block, 0, outputLen - (inIndex - inOff));
ctrCipher.processBlock(block, 0, block, 0);
- System.arraycopy(block, 0, output, outOff, output.length - outOff);
+ System.arraycopy(block, 0, output, outIndex, outputLen - (inIndex - inOff));
byte[] calculatedMacBlock = new byte[blockSize];
- calculateMac(output, 0, output.length, calculatedMacBlock);
+ calculateMac(output, outOff, outputLen, calculatedMacBlock);
if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock))
{
@@ -275,7 +337,7 @@ public class CCMBlockCipher
}
}
- return output;
+ return outputLen;
}
private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
@@ -344,8 +406,7 @@ public class CCMBlockCipher
}
if (associatedText.size() > 0)
{
- byte[] tmp = associatedText.toByteArray();
- cMac.update(tmp, 0, tmp.length);
+ cMac.update(associatedText.getBuffer(), 0, associatedText.size());
}
extra = (extra + textLength) % 16;
@@ -375,4 +436,17 @@ public class CCMBlockCipher
{
return getAssociatedTextLength() > 0;
}
+
+ private class ExposedByteArrayOutputStream
+ extends ByteArrayOutputStream
+ {
+ public ExposedByteArrayOutputStream()
+ {
+ }
+
+ public byte[] getBuffer()
+ {
+ return this.buf;
+ }
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
index d0fb9bb..a885169 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -4,6 +4,7 @@ import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
/**
* implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
@@ -246,6 +247,16 @@ public class CFBBlockCipher
}
/**
+ * Return the current state of the initialisation vector.
+ *
+ * @return current IV
+ */
+ public byte[] getCurrentIV()
+ {
+ return Arrays.clone(cfbV);
+ }
+
+ /**
* reset the chaining vector back to the IV and reset the underlying
* cipher.
*/
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
index b8e5b61..5388b40 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
@@ -22,8 +22,9 @@ public class CTSBlockCipher
public CTSBlockCipher(
BlockCipher cipher)
{
- if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher))
+ if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher) || (cipher instanceof SICBlockCipher))
{
+ // TODO: This is broken - need to introduce marker interface to differentiate block cipher primitive from mode?
throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers");
}
@@ -72,7 +73,7 @@ public class CTSBlockCipher
}
/**
- * process a single byte, producing an output block if neccessary.
+ * process a single byte, producing an output block if necessary.
*
* @param in the input byte.
* @param out the space for any output that might be produced.
@@ -200,60 +201,81 @@ public class CTSBlockCipher
if (forEncryption)
{
- cipher.processBlock(buf, 0, block, 0);
-
if (bufOff < blockSize)
{
throw new DataLengthException("need at least one block of input for CTS");
}
- for (int i = bufOff; i != buf.length; i++)
- {
- buf[i] = block[i - blockSize];
- }
-
- for (int i = blockSize; i != bufOff; i++)
- {
- buf[i] ^= block[i - blockSize];
- }
+ cipher.processBlock(buf, 0, block, 0);
- if (cipher instanceof CBCBlockCipher)
+ if (bufOff > blockSize)
{
- BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
-
- c.processBlock(buf, blockSize, out, outOff);
+ for (int i = bufOff; i != buf.length; i++)
+ {
+ buf[i] = block[i - blockSize];
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ buf[i] ^= block[i - blockSize];
+ }
+
+ if (cipher instanceof CBCBlockCipher)
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, blockSize, out, outOff);
+ }
+ else
+ {
+ cipher.processBlock(buf, blockSize, out, outOff);
+ }
+
+ System.arraycopy(block, 0, out, outOff + blockSize, len);
}
else
{
- cipher.processBlock(buf, blockSize, out, outOff);
+ System.arraycopy(block, 0, out, outOff, blockSize);
}
-
- System.arraycopy(block, 0, out, outOff + blockSize, len);
}
else
{
+ if (bufOff < blockSize)
+ {
+ throw new DataLengthException("need at least one block of input for CTS");
+ }
+
byte[] lastBlock = new byte[blockSize];
- if (cipher instanceof CBCBlockCipher)
+ if (bufOff > blockSize)
{
- BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
-
- c.processBlock(buf, 0, block, 0);
+ if (cipher instanceof CBCBlockCipher)
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, 0, block, 0);
+ }
+ else
+ {
+ cipher.processBlock(buf, 0, block, 0);
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+ }
+
+ System.arraycopy(buf, blockSize, block, 0, len);
+
+ cipher.processBlock(block, 0, out, outOff);
+ System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
}
else
{
cipher.processBlock(buf, 0, block, 0);
- }
- for (int i = blockSize; i != bufOff; i++)
- {
- lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+ System.arraycopy(block, 0, out, outOff, blockSize);
}
-
- System.arraycopy(buf, blockSize, block, 0, len);
-
- cipher.processBlock(block, 0, out, outOff);
- System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
}
int offset = bufOff;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
index 4999caa..8f74000 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
@@ -123,16 +123,10 @@ public class EAXBlockCipher
mac.update(nonce, 0, nonce.length);
mac.doFinal(nonceMac, 0);
- tag[blockSize - 1] = hTAG;
- mac.update(tag, 0, blockSize);
-
- if (initialAssociatedText != null)
- {
- processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
- }
-
// Same BlockCipher underlies this and the mac, so reuse last key on cipher
cipher.init(true, new ParametersWithIV(null, nonceMac));
+
+ reset();
}
private void initCipher()
@@ -206,7 +200,7 @@ public class EAXBlockCipher
{
if (cipherInitialized)
{
- throw new IllegalStateException("AAD data cannot be added after encryption/decription processing has begun.");
+ throw new IllegalStateException("AAD data cannot be added after encryption/decryption processing has begun.");
}
mac.update(in, inOff, len);
}
@@ -246,6 +240,10 @@ public class EAXBlockCipher
if (forEncryption)
{
+ if (out.length < (outOff + extra))
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
cipher.processBlock(bufBlock, 0, tmp, 0);
cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
@@ -263,6 +261,10 @@ public class EAXBlockCipher
}
else
{
+ if (extra < macSize)
+ {
+ throw new InvalidCipherTextException("data too short");
+ }
if (extra > macSize)
{
mac.update(bufBlock, 0, extra - macSize);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java
new file mode 100644
index 0000000..887c169
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+
+/**
+ * An implementation of the GOST CFB mode with CryptoPro key meshing as described in RFC 4357.
+ */
+public class GCFBBlockCipher
+ implements BlockCipher
+{
+ private static final byte[] C =
+ {
+ 0x69, 0x00, 0x72, 0x22, 0x64, (byte)0xC9, 0x04, 0x23,
+ (byte)0x8D, 0x3A, (byte)0xDB, (byte)0x96, 0x46, (byte)0xE9, 0x2A, (byte)0xC4,
+ 0x18, (byte)0xFE, (byte)0xAC, (byte)0x94, 0x00, (byte)0xED, 0x07, 0x12,
+ (byte)0xC0, (byte)0x86, (byte)0xDC, (byte)0xC2, (byte)0xEF, 0x4C, (byte)0xA9, 0x2B
+ };
+
+ private final CFBBlockCipher cfbEngine;
+
+ private KeyParameter key;
+ private long counter = 0;
+ private boolean forEncryption;
+
+ public GCFBBlockCipher(BlockCipher engine)
+ {
+ this.cfbEngine = new CFBBlockCipher(engine, engine.getBlockSize() * 8);
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ counter = 0;
+ cfbEngine.init(forEncryption, params);
+
+ this.forEncryption = forEncryption;
+
+ if (params instanceof ParametersWithIV)
+ {
+ params = ((ParametersWithIV)params).getParameters();
+ }
+
+ if (params instanceof ParametersWithRandom)
+ {
+ params = ((ParametersWithRandom)params).getParameters();
+ }
+
+ if (params instanceof ParametersWithSBox)
+ {
+ params = ((ParametersWithSBox)params).getParameters();
+ }
+
+ key = (KeyParameter)params;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "G" + cfbEngine.getAlgorithmName();
+ }
+
+ public int getBlockSize()
+ {
+ return cfbEngine.getBlockSize();
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (counter > 0 && counter % 1024 == 0)
+ {
+ BlockCipher base = cfbEngine.getUnderlyingCipher();
+
+ base.init(false, key);
+
+ byte[] nextKey = new byte[32];
+
+ base.processBlock(C, 0, nextKey, 0);
+ base.processBlock(C, 8, nextKey, 8);
+ base.processBlock(C, 16, nextKey, 16);
+ base.processBlock(C, 24, nextKey, 24);
+
+ key = new KeyParameter(nextKey);
+
+ byte[] iv = new byte[8];
+
+ base.init(true, key);
+
+ base.processBlock(cfbEngine.getCurrentIV(), 0, iv, 0);
+
+ cfbEngine.init(forEncryption, new ParametersWithIV(key, iv));
+ }
+
+ counter += cfbEngine.getBlockSize();
+
+ return cfbEngine.processBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ counter = 0;
+ cfbEngine.reset();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
index 1178974..0e66cf3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
@@ -206,6 +206,9 @@ public class GOFBBlockCipher
*/
public void reset()
{
+ firstStep = true;
+ N3 = 0;
+ N4 = 0;
System.arraycopy(IV, 0, ofbV, 0, IV.length);
cipher.reset();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
index d4d2910..6dc7148 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
@@ -13,7 +13,7 @@ import org.bouncycastle.util.Arrays;
/**
* An implementation of the "work in progress" Internet-Draft <a
- * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-00">The OCB Authenticated-Encryption
+ * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-03">The OCB Authenticated-Encryption
* Algorithm</a>, licensed per:
* <p/>
* <blockquote> <a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for
@@ -111,7 +111,6 @@ public class OCBBlockCipher
public void init(boolean forEncryption, CipherParameters parameters)
throws IllegalArgumentException
{
-
this.forEncryption = forEncryption;
this.macBlock = null;
@@ -156,23 +155,18 @@ public class OCBBlockCipher
N = new byte[0];
}
- if (N.length > 16 || (N.length == 16 && (N[0] & 0x80) != 0))
+ if (N.length > 15)
{
- /*
- * NOTE: We don't just ignore bit 128 because it would hide from the caller the fact
- * that two nonces differing only in bit 128 are not different.
- */
- throw new IllegalArgumentException("IV must be no more than 127 bits");
+ throw new IllegalArgumentException("IV must be no more than 15 bytes");
}
/*
* KEY-DEPENDENT INITIALISATION
*/
- // if keyParam is null we're reusing the last key.
- if (keyParameter != null)
+ if (keyParameter == null)
{
- // TODO
+ // TODO If 'keyParameter' is null we're re-using the last key.
}
// hashCipher always used in forward mode
@@ -193,17 +187,10 @@ public class OCBBlockCipher
byte[] nonce = new byte[16];
System.arraycopy(N, 0, nonce, nonce.length - N.length, N.length);
- if (N.length == 16)
- {
- nonce[0] &= 0x80;
- }
- else
- {
- nonce[15 - N.length] = 1;
- }
+ nonce[0] = (byte)(macSize << 4);
+ nonce[15 - N.length] |= 1;
int bottom = nonce[15] & 0x3F;
- // System.out.println("bottom: " + bottom);
byte[] Ktop = new byte[16];
nonce[15] &= 0xC0;
@@ -314,7 +301,6 @@ public class OCBBlockCipher
public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
throws DataLengthException
{
-
int resultLen = 0;
for (int i = 0; i < len; ++i)
@@ -334,7 +320,6 @@ public class OCBBlockCipher
throws IllegalStateException,
InvalidCipherTextException
{
-
/*
* For decryption, get the tag from the end of the message
*/
@@ -483,7 +468,6 @@ public class OCBBlockCipher
protected void reset(boolean clearMac)
{
-
hashCipher.reset();
mainCipher.reset();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java
new file mode 100644
index 0000000..b34432a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java
@@ -0,0 +1,269 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
+ * be used to produce cipher text which is the same length as the plain text.
+ * <p>
+ * This version applies the CTS algorithm from one block up, rather than following the errata update issued in 2004, where CTS mode is applied
+ * from greater than 1 block up and the first block is processed using CBC mode.
+ * </p>
+ */
+public class OldCTSBlockCipher
+ extends BufferedBlockCipher
+{
+ private int blockSize;
+
+ /**
+ * Create a buffered block cipher that uses Cipher Text Stealing
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ */
+ public OldCTSBlockCipher(
+ BlockCipher cipher)
+ {
+ if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher))
+ {
+ throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers");
+ }
+
+ this.cipher = cipher;
+
+ blockSize = cipher.getBlockSize();
+
+ buf = new byte[blockSize * 2];
+ bufOff = 0;
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public int getUpdateOutputSize(
+ int len)
+ {
+ int total = len + bufOff;
+ int leftOver = total % buf.length;
+
+ if (leftOver == 0)
+ {
+ return total - buf.length;
+ }
+
+ return total - leftOver;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public int getOutputSize(
+ int len)
+ {
+ return len + bufOff;
+ }
+
+ /**
+ * process a single byte, producing an output block if necessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception org.bouncycastle.crypto.DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processByte(
+ byte in,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ int resultLen = 0;
+
+ if (bufOff == buf.length)
+ {
+ resultLen = cipher.processBlock(buf, 0, out, outOff);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ bufOff = blockSize;
+ }
+
+ buf[bufOff++] = in;
+
+ return resultLen;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception org.bouncycastle.crypto.DataLengthException if there isn't enough space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ */
+ public int processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ if (len < 0)
+ {
+ throw new IllegalArgumentException("Can't have a negative input length!");
+ }
+
+ int blockSize = getBlockSize();
+ int length = getUpdateOutputSize(len);
+
+ if (length > 0)
+ {
+ if ((outOff + length) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.length - bufOff;
+
+ if (len > gapLen)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+ resultLen += cipher.processBlock(buf, 0, out, outOff);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ bufOff = blockSize;
+
+ len -= gapLen;
+ inOff += gapLen;
+
+ while (len > blockSize)
+ {
+ System.arraycopy(in, inOff, buf, bufOff, blockSize);
+ resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+ System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+ len -= blockSize;
+ inOff += blockSize;
+ }
+ }
+
+ System.arraycopy(in, inOff, buf, bufOff, len);
+
+ bufOff += len;
+
+ return resultLen;
+ }
+
+ /**
+ * Process the last block in the buffer.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception org.bouncycastle.crypto.DataLengthException if there is insufficient space in out for
+ * the output.
+ * @exception IllegalStateException if the underlying cipher is not
+ * initialised.
+ * @exception org.bouncycastle.crypto.InvalidCipherTextException if cipher text decrypts wrongly (in
+ * case the exception will never get thrown).
+ */
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ throws DataLengthException, IllegalStateException, InvalidCipherTextException
+ {
+ if (bufOff + outOff > out.length)
+ {
+ throw new DataLengthException("output buffer to small in doFinal");
+ }
+
+ int blockSize = cipher.getBlockSize();
+ int len = bufOff - blockSize;
+ byte[] block = new byte[blockSize];
+
+ if (forEncryption)
+ {
+ cipher.processBlock(buf, 0, block, 0);
+
+ if (bufOff < blockSize)
+ {
+ throw new DataLengthException("need at least one block of input for CTS");
+ }
+
+ for (int i = bufOff; i != buf.length; i++)
+ {
+ buf[i] = block[i - blockSize];
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ buf[i] ^= block[i - blockSize];
+ }
+
+ if (cipher instanceof CBCBlockCipher)
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, blockSize, out, outOff);
+ }
+ else
+ {
+ cipher.processBlock(buf, blockSize, out, outOff);
+ }
+
+ System.arraycopy(block, 0, out, outOff + blockSize, len);
+ }
+ else
+ {
+ byte[] lastBlock = new byte[blockSize];
+
+ if (cipher instanceof CBCBlockCipher)
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, 0, block, 0);
+ }
+ else
+ {
+ cipher.processBlock(buf, 0, block, 0);
+ }
+
+ for (int i = blockSize; i != bufOff; i++)
+ {
+ lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+ }
+
+ System.arraycopy(buf, blockSize, block, 0, len);
+
+ cipher.processBlock(block, 0, out, outOff);
+ System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
+ }
+
+ int offset = bufOff;
+
+ reset();
+
+ return offset;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
index 18e612b..4dee63a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
@@ -220,13 +220,13 @@ public class PGPCFBBlockCipher
throw new DataLengthException("input buffer too short");
}
- if ((outOff + blockSize) > out.length)
- {
- throw new DataLengthException("output buffer too short");
- }
-
if (count == 0)
{
+ if ((outOff + 2 * blockSize + 2) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
cipher.processBlock(FR, 0, FRE, 0);
for (int n = 0; n < blockSize; n++)
@@ -258,6 +258,11 @@ public class PGPCFBBlockCipher
}
else if (count >= blockSize + 2)
{
+ if ((outOff + blockSize) > out.length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+
cipher.processBlock(FR, 0, FRE, 0);
for (int n = 0; n < blockSize; n++)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
index f2be2fc..fc25810 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
@@ -4,21 +4,21 @@ import org.bouncycastle.util.Arrays;
public class BasicGCMExponentiator implements GCMExponentiator
{
- private byte[] x;
+ private int[] x;
public void init(byte[] x)
{
- this.x = Arrays.clone(x);
+ this.x = GCMUtil.asInts(x);
}
public void exponentiateX(long pow, byte[] output)
{
// Initial value is little-endian 1
- byte[] y = GCMUtil.oneAsBytes();
+ int[] y = GCMUtil.oneAsInts();
if (pow > 0)
{
- byte[] powX = Arrays.clone(x);
+ int[] powX = Arrays.clone(x);
do
{
if ((pow & 1L) != 0)
@@ -31,6 +31,6 @@ public class BasicGCMExponentiator implements GCMExponentiator
while (pow > 0);
}
- System.arraycopy(y, 0, output, 0, 16);
+ GCMUtil.asBytes(y, output);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
index 4875301..3031a44 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -5,6 +5,32 @@ import org.bouncycastle.util.Arrays;
abstract class GCMUtil
{
+ private static final int E1 = 0xe1000000;
+ private static final byte E1B = (byte)0xe1;
+ private static final long E1L = (E1 & 0xFFFFFFFFL) << 24;
+
+ private static int[] generateLookup()
+ {
+ int[] lookup = new int[256];
+
+ for (int c = 0; c < 256; ++c)
+ {
+ int v = 0;
+ for (int i = 7; i >= 0; --i)
+ {
+ if ((c & (1 << i)) != 0)
+ {
+ v ^= (E1 >>> (7 - i));
+ }
+ }
+ lookup[c] = v;
+ }
+
+ return lookup;
+ }
+
+ private static final int[] LOOKUP = generateLookup();
+
static byte[] oneAsBytes()
{
byte[] tmp = new byte[16];
@@ -15,78 +41,155 @@ abstract class GCMUtil
static int[] oneAsInts()
{
int[] tmp = new int[4];
- tmp[0] = 0x80000000;
+ tmp[0] = 1 << 31;
+ return tmp;
+ }
+
+ static long[] oneAsLongs()
+ {
+ long[] tmp = new long[2];
+ tmp[0] = 1L << 63;
return tmp;
}
- static byte[] asBytes(int[] ns)
+ static byte[] asBytes(int[] x)
+ {
+ byte[] z = new byte[16];
+ Pack.intToBigEndian(x, z, 0);
+ return z;
+ }
+
+ static void asBytes(int[] x, byte[] z)
+ {
+ Pack.intToBigEndian(x, z, 0);
+ }
+
+ static byte[] asBytes(long[] x)
+ {
+ byte[] z = new byte[16];
+ Pack.longToBigEndian(x, z, 0);
+ return z;
+ }
+
+ static void asBytes(long[] x, byte[] z)
+ {
+ Pack.longToBigEndian(x, z, 0);
+ }
+
+ static int[] asInts(byte[] x)
+ {
+ int[] z = new int[4];
+ Pack.bigEndianToInt(x, 0, z);
+ return z;
+ }
+
+ static void asInts(byte[] x, int[] z)
{
- byte[] output = new byte[16];
- Pack.intToBigEndian(ns, output, 0);
- return output;
+ Pack.bigEndianToInt(x, 0, z);
}
- static int[] asInts(byte[] bs)
+ static long[] asLongs(byte[] x)
{
- int[] output = new int[4];
- Pack.bigEndianToInt(bs, 0, output);
- return output;
+ long[] z = new long[2];
+ Pack.bigEndianToLong(x, 0, z);
+ return z;
}
- static void asInts(byte[] bs, int[] output)
+ static void asLongs(byte[] x, long[] z)
{
- Pack.bigEndianToInt(bs, 0, output);
+ Pack.bigEndianToLong(x, 0, z);
}
- static void multiply(byte[] block, byte[] val)
+ static void multiply(byte[] x, byte[] y)
{
- byte[] tmp = Arrays.clone(block);
- byte[] c = new byte[16];
+ byte[] r0 = Arrays.clone(x);
+ byte[] r1 = new byte[16];
for (int i = 0; i < 16; ++i)
{
- byte bits = val[i];
+ byte bits = y[i];
for (int j = 7; j >= 0; --j)
{
if ((bits & (1 << j)) != 0)
{
- xor(c, tmp);
+ xor(r1, r0);
}
- boolean lsb = (tmp[15] & 1) != 0;
- shiftRight(tmp);
- if (lsb)
+ if (shiftRight(r0) != 0)
{
- // R = new byte[]{ 0xe1, ... };
-// GCMUtil.xor(v, R);
- tmp[0] ^= (byte)0xe1;
+ r0[0] ^= E1B;
}
}
}
- System.arraycopy(c, 0, block, 0, 16);
+ System.arraycopy(r1, 0, x, 0, 16);
+ }
+
+ static void multiply(int[] x, int[] y)
+ {
+ int[] r0 = Arrays.clone(x);
+ int[] r1 = new int[4];
+
+ for (int i = 0; i < 4; ++i)
+ {
+ int bits = y[i];
+ for (int j = 31; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(r1, r0);
+ }
+
+ if (shiftRight(r0) != 0)
+ {
+ r0[0] ^= E1;
+ }
+ }
+ }
+
+ System.arraycopy(r1, 0, x, 0, 4);
+ }
+
+ static void multiply(long[] x, long[] y)
+ {
+ long[] r0 = new long[]{ x[0], x[1] };
+ long[] r1 = new long[2];
+
+ for (int i = 0; i < 2; ++i)
+ {
+ long bits = y[i];
+ for (int j = 63; j >= 0; --j)
+ {
+ if ((bits & (1L << j)) != 0)
+ {
+ xor(r1, r0);
+ }
+
+ if (shiftRight(r0) != 0)
+ {
+ r0[0] ^= E1L;
+ }
+ }
+ }
+
+ x[0] = r1[0];
+ x[1] = r1[1];
}
// P is the value with only bit i=1 set
static void multiplyP(int[] x)
{
- boolean lsb = (x[3] & 1) != 0;
- shiftRight(x);
- if (lsb)
+ if (shiftRight(x) != 0)
{
- // R = new int[]{ 0xe1000000, 0, 0, 0 };
-// xor(v, R);
- x[0] ^= 0xe1000000;
+ x[0] ^= E1;
}
}
- static void multiplyP(int[] x, int[] output)
+ static void multiplyP(int[] x, int[] y)
{
- boolean lsb = (x[3] & 1) != 0;
- shiftRight(x, output);
- if (lsb)
+ if (shiftRight(x, y) != 0)
{
- output[0] ^= 0xe1000000;
+ y[0] ^= E1;
}
}
@@ -98,163 +201,257 @@ abstract class GCMUtil
// multiplyP(x);
// }
- int lsw = x[3];
- shiftRightN(x, 8);
- for (int i = 7; i >= 0; --i)
- {
- if ((lsw & (1 << i)) != 0)
- {
- x[0] ^= (0xe1000000 >>> (7 - i));
- }
- }
+ int c = shiftRightN(x, 8);
+ x[0] ^= LOOKUP[c >>> 24];
}
- static void multiplyP8(int[] x, int[] output)
+ static void multiplyP8(int[] x, int[] y)
{
- int lsw = x[3];
- shiftRightN(x, 8, output);
- for (int i = 7; i >= 0; --i)
- {
- if ((lsw & (1 << i)) != 0)
- {
- output[0] ^= (0xe1000000 >>> (7 - i));
- }
- }
+ int c = shiftRightN(x, 8, y);
+ y[0] ^= LOOKUP[c >>> 24];
}
- static void shiftRight(byte[] block)
+ static byte shiftRight(byte[] x)
{
- int i = 0;
- int bit = 0;
- for (;;)
+// int c = 0;
+// for (int i = 0; i < 16; ++i)
+// {
+// int b = x[i] & 0xff;
+// x[i] = (byte)((b >>> 1) | c);
+// c = (b & 1) << 7;
+// }
+// return (byte)c;
+
+ int i = 0, c = 0;
+ do
{
- int b = block[i] & 0xff;
- block[i] = (byte) ((b >>> 1) | bit);
- if (++i == 16)
- {
- break;
- }
- bit = (b & 1) << 7;
+ int b = x[i] & 0xff;
+ x[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ x[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ x[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ x[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
}
+ while (i < 16);
+ return (byte)c;
}
- static void shiftRight(byte[] block, byte[] output)
+ static byte shiftRight(byte[] x, byte[] z)
{
- int i = 0;
- int bit = 0;
- for (;;)
+// int c = 0;
+// for (int i = 0; i < 16; ++i)
+// {
+// int b = x[i] & 0xff;
+// z[i] = (byte) ((b >>> 1) | c);
+// c = (b & 1) << 7;
+// }
+// return (byte) c;
+
+ int i = 0, c = 0;
+ do
{
- int b = block[i] & 0xff;
- output[i] = (byte) ((b >>> 1) | bit);
- if (++i == 16)
- {
- break;
- }
- bit = (b & 1) << 7;
+ int b = x[i] & 0xff;
+ z[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ z[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ z[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
+ b = x[i] & 0xff;
+ z[i++] = (byte)((b >>> 1) | c);
+ c = (b & 1) << 7;
}
+ while (i < 16);
+ return (byte)c;
}
- static void shiftRight(int[] block)
+ static int shiftRight(int[] x)
{
- int i = 0;
- int bit = 0;
- for (;;)
- {
- int b = block[i];
- block[i] = (b >>> 1) | bit;
- if (++i == 4)
- {
- break;
- }
- bit = b << 31;
- }
+// int c = 0;
+// for (int i = 0; i < 4; ++i)
+// {
+// int b = x[i];
+// x[i] = (b >>> 1) | c;
+// c = b << 31;
+// }
+// return c;
+
+ int b = x[0];
+ x[0] = b >>> 1;
+ int c = b << 31;
+ b = x[1];
+ x[1] = (b >>> 1) | c;
+ c = b << 31;
+ b = x[2];
+ x[2] = (b >>> 1) | c;
+ c = b << 31;
+ b = x[3];
+ x[3] = (b >>> 1) | c;
+ return b << 31;
}
- static void shiftRight(int[] block, int[] output)
+ static int shiftRight(int[] x, int[] z)
{
- int i = 0;
- int bit = 0;
- for (;;)
- {
- int b = block[i];
- output[i] = (b >>> 1) | bit;
- if (++i == 4)
- {
- break;
- }
- bit = b << 31;
- }
+// int c = 0;
+// for (int i = 0; i < 4; ++i)
+// {
+// int b = x[i];
+// z[i] = (b >>> 1) | c;
+// c = b << 31;
+// }
+// return c;
+
+ int b = x[0];
+ z[0] = b >>> 1;
+ int c = b << 31;
+ b = x[1];
+ z[1] = (b >>> 1) | c;
+ c = b << 31;
+ b = x[2];
+ z[2] = (b >>> 1) | c;
+ c = b << 31;
+ b = x[3];
+ z[3] = (b >>> 1) | c;
+ return b << 31;
}
- static void shiftRightN(int[] block, int n)
+ static long shiftRight(long[] x)
{
- int i = 0;
- int bits = 0;
- for (;;)
- {
- int b = block[i];
- block[i] = (b >>> n) | bits;
- if (++i == 4)
- {
- break;
- }
- bits = b << (32 - n);
- }
+ long b = x[0];
+ x[0] = b >>> 1;
+ long c = b << 63;
+ b = x[1];
+ x[1] = (b >>> 1) | c;
+ return b << 63;
+ }
+
+ static long shiftRight(long[] x, long[] z)
+ {
+ long b = x[0];
+ z[0] = b >>> 1;
+ long c = b << 63;
+ b = x[1];
+ z[1] = (b >>> 1) | c;
+ return b << 63;
+ }
+
+ static int shiftRightN(int[] x, int n)
+ {
+// int c = 0, nInv = 32 - n;
+// for (int i = 0; i < 4; ++i)
+// {
+// int b = x[i];
+// x[i] = (b >>> n) | c;
+// c = b << nInv;
+// }
+// return c;
+
+ int b = x[0], nInv = 32 - n;
+ x[0] = b >>> n;
+ int c = b << nInv;
+ b = x[1];
+ x[1] = (b >>> n) | c;
+ c = b << nInv;
+ b = x[2];
+ x[2] = (b >>> n) | c;
+ c = b << nInv;
+ b = x[3];
+ x[3] = (b >>> n) | c;
+ return b << nInv;
}
- static void shiftRightN(int[] block, int n, int[] output)
+ static int shiftRightN(int[] x, int n, int[] z)
+ {
+// int c = 0, nInv = 32 - n;
+// for (int i = 0; i < 4; ++i)
+// {
+// int b = x[i];
+// z[i] = (b >>> n) | c;
+// c = b << nInv;
+// }
+// return c;
+
+ int b = x[0], nInv = 32 - n;
+ z[0] = b >>> n;
+ int c = b << nInv;
+ b = x[1];
+ z[1] = (b >>> n) | c;
+ c = b << nInv;
+ b = x[2];
+ z[2] = (b >>> n) | c;
+ c = b << nInv;
+ b = x[3];
+ z[3] = (b >>> n) | c;
+ return b << nInv;
+ }
+
+ static void xor(byte[] x, byte[] y)
{
int i = 0;
- int bits = 0;
- for (;;)
+ do
{
- int b = block[i];
- output[i] = (b >>> n) | bits;
- if (++i == 4)
- {
- break;
- }
- bits = b << (32 - n);
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
+ x[i] ^= y[i]; ++i;
}
+ while (i < 16);
}
- static void xor(byte[] block, byte[] val)
+ static void xor(byte[] x, byte[] y, int yOff, int yLen)
{
- for (int i = 15; i >= 0; --i)
+ while (yLen-- > 0)
{
- block[i] ^= val[i];
+ x[yLen] ^= y[yOff + yLen];
}
}
- static void xor(byte[] block, byte[] val, int off, int len)
+ static void xor(byte[] x, byte[] y, byte[] z)
{
- while (len-- > 0)
+ int i = 0;
+ do
{
- block[len] ^= val[off + len];
+ z[i] = (byte)(x[i] ^ y[i]); ++i;
+ z[i] = (byte)(x[i] ^ y[i]); ++i;
+ z[i] = (byte)(x[i] ^ y[i]); ++i;
+ z[i] = (byte)(x[i] ^ y[i]); ++i;
}
+ while (i < 16);
}
- static void xor(byte[] block, byte[] val, byte[] output)
+ static void xor(int[] x, int[] y)
{
- for (int i = 15; i >= 0; --i)
- {
- output[i] = (byte)(block[i] ^ val[i]);
- }
+ x[0] ^= y[0];
+ x[1] ^= y[1];
+ x[2] ^= y[2];
+ x[3] ^= y[3];
}
- static void xor(int[] block, int[] val)
+ static void xor(int[] x, int[] y, int[] z)
{
- for (int i = 3; i >= 0; --i)
- {
- block[i] ^= val[i];
- }
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
+ z[2] = x[2] ^ y[2];
+ z[3] = x[3] ^ y[3];
}
- static void xor(int[] block, int[] val, int[] output)
+ static void xor(long[] x, long[] y)
{
- for (int i = 3; i >= 0; --i)
- {
- output[i] = block[i] ^ val[i];
- }
+ x[0] ^= y[0];
+ x[1] ^= y[1];
+ }
+
+ static void xor(long[] x, long[] y, long[] z)
+ {
+ z[0] = x[0] ^ y[0];
+ z[1] = x[1] ^ y[1];
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
index a051208..6eff4e3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -12,31 +12,32 @@ public class Tables1kGCMExponentiator implements GCMExponentiator
public void init(byte[] x)
{
- if (lookupPowX2 != null && Arrays.areEqual(x, (byte[])lookupPowX2.elementAt(0)))
+ int[] y = GCMUtil.asInts(x);
+ if (lookupPowX2 != null && Arrays.areEqual(y, (int[])lookupPowX2.elementAt(0)))
{
return;
}
lookupPowX2 = new Vector(8);
- lookupPowX2.addElement(Arrays.clone(x));
+ lookupPowX2.addElement(y);
}
public void exponentiateX(long pow, byte[] output)
{
- byte[] y = GCMUtil.oneAsBytes();
+ int[] y = GCMUtil.oneAsInts();
int bit = 0;
while (pow > 0)
{
if ((pow & 1L) != 0)
{
ensureAvailable(bit);
- GCMUtil.multiply(y, (byte[])lookupPowX2.elementAt(bit));
+ GCMUtil.multiply(y, (int[])lookupPowX2.elementAt(bit));
}
++bit;
pow >>>= 1;
}
- System.arraycopy(y, 0, output, 0, 16);
+ GCMUtil.asBytes(y, output);
}
private void ensureAvailable(int bit)
@@ -44,7 +45,7 @@ public class Tables1kGCMExponentiator implements GCMExponentiator
int count = lookupPowX2.size();
if (count <= bit)
{
- byte[] tmp = (byte[])lookupPowX2.elementAt(count - 1);
+ int[] tmp = (int[])lookupPowX2.elementAt(count - 1);
do
{
tmp = Arrays.clone(tmp);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html
deleted file mode 100644
index 5402df4..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Modes for symmetric ciphers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/package.html
deleted file mode 100644
index ee5487f..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Base classes for the lightweight API.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html
deleted file mode 100644
index 2b82e60..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Paddings for symmetric ciphers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
index 05a1327..9cc6e72 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
@@ -41,7 +41,7 @@ public class ECDomainParameters
byte[] seed)
{
this.curve = curve;
- this.G = G;
+ this.G = G.normalize();
this.n = n;
this.h = h;
this.seed = seed;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
index 5fbea19..b6b3fb6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
@@ -12,7 +12,7 @@ public class ECPublicKeyParameters
ECDomainParameters params)
{
super(false, params);
- this.Q = Q;
+ this.Q = Q.normalize();
}
public ECPoint getQ()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
new file mode 100644
index 0000000..0eb6cb7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.util.Arrays;
+
+public final class KDFCounterParameters
+ implements DerivationParameters
+{
+
+ private final byte[] ki;
+ private final byte[] fixedInputData;
+ private final int r;
+
+ public KDFCounterParameters(byte[] ki, byte[] fixedInputData, int r)
+ {
+ if (ki == null)
+ {
+ throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+ }
+ this.ki = Arrays.clone(ki);
+
+ if (fixedInputData == null)
+ {
+ this.fixedInputData = new byte[0];
+ }
+ else
+ {
+ this.fixedInputData = Arrays.clone(fixedInputData);
+ }
+
+ if (r != 8 && r != 16 && r != 24 && r != 32)
+ {
+ throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+ }
+ this.r = r;
+ }
+
+ public byte[] getKI()
+ {
+ return ki;
+ }
+
+ public byte[] getFixedInputData()
+ {
+ return Arrays.clone(fixedInputData);
+ }
+
+ public int getR()
+ {
+ return r;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java
new file mode 100644
index 0000000..383678a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Note that counter is only supported at the location presented in the
+ * NIST SP 800-108 specification, not in the additional locations present
+ * in the CAVP test vectors.
+ */
+public final class KDFDoublePipelineIterationParameters
+ implements DerivationParameters
+{
+
+ // could be any valid value, using 32, don't know why
+ private static final int UNUSED_R = 32;
+
+ private final byte[] ki;
+ private final boolean useCounter;
+ private final int r;
+ private final byte[] fixedInputData;
+
+ private KDFDoublePipelineIterationParameters(byte[] ki, byte[] fixedInputData, int r, boolean useCounter)
+ {
+ if (ki == null)
+ {
+ throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+ }
+ this.ki = Arrays.clone(ki);
+
+ if (fixedInputData == null)
+ {
+ this.fixedInputData = new byte[0];
+ }
+ else
+ {
+ this.fixedInputData = Arrays.clone(fixedInputData);
+ }
+
+ if (r != 8 && r != 16 && r != 24 && r != 32)
+ {
+ throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+ }
+ this.r = r;
+
+ this.useCounter = useCounter;
+ }
+
+ public static KDFDoublePipelineIterationParameters createWithCounter(
+ byte[] ki, byte[] fixedInputData, int r)
+ {
+ return new KDFDoublePipelineIterationParameters(ki, fixedInputData, r, true);
+ }
+
+ public static KDFDoublePipelineIterationParameters createWithoutCounter(
+ byte[] ki, byte[] fixedInputData)
+ {
+ return new KDFDoublePipelineIterationParameters(ki, fixedInputData, UNUSED_R, false);
+ }
+
+ public byte[] getKI()
+ {
+ return ki;
+ }
+
+ public boolean useCounter()
+ {
+ return useCounter;
+ }
+
+ public int getR()
+ {
+ return r;
+ }
+
+ public byte[] getFixedInputData()
+ {
+ return Arrays.clone(fixedInputData);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java
new file mode 100644
index 0000000..8a6e7f5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Note that counter is only supported at the location presented in the
+ * NIST SP 800-108 specification, not in the additional locations present
+ * in the CAVP test vectors.
+ */
+public final class KDFFeedbackParameters
+ implements DerivationParameters
+{
+
+ // could be any valid value, using 32, don't know why
+ private static final int UNUSED_R = -1;
+
+ private final byte[] ki;
+ private final byte[] iv;
+ private final boolean useCounter;
+ private final int r;
+ private final byte[] fixedInputData;
+
+ private KDFFeedbackParameters(byte[] ki, byte[] iv, byte[] fixedInputData, int r, boolean useCounter)
+ {
+ if (ki == null)
+ {
+ throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+ }
+ this.ki = Arrays.clone(ki);
+
+ if (fixedInputData == null)
+ {
+ this.fixedInputData = new byte[0];
+ }
+ else
+ {
+ this.fixedInputData = Arrays.clone(fixedInputData);
+ }
+
+ this.r = r;
+
+ if (iv == null)
+ {
+ this.iv = new byte[0];
+ }
+ else
+ {
+ this.iv = Arrays.clone(iv);
+ }
+
+ this.useCounter = useCounter;
+ }
+
+ public static KDFFeedbackParameters createWithCounter(
+ byte[] ki, final byte[] iv, byte[] fixedInputData, int r)
+ {
+ if (r != 8 && r != 16 && r != 24 && r != 32)
+ {
+ throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+ }
+
+ return new KDFFeedbackParameters(ki, iv, fixedInputData, r, true);
+ }
+
+ public static KDFFeedbackParameters createWithoutCounter(
+ byte[] ki, final byte[] iv, byte[] fixedInputData)
+ {
+ return new KDFFeedbackParameters(ki, iv, fixedInputData, UNUSED_R, false);
+ }
+
+ public byte[] getKI()
+ {
+ return ki;
+ }
+
+ public byte[] getIV()
+ {
+ return iv;
+ }
+
+ public boolean useCounter()
+ {
+ return useCounter;
+ }
+
+ public int getR()
+ {
+ return r;
+ }
+
+ public byte[] getFixedInputData()
+ {
+ return Arrays.clone(fixedInputData);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
new file mode 100644
index 0000000..76241ee
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
@@ -0,0 +1,293 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.digests.SkeinDigest;
+import org.bouncycastle.crypto.digests.SkeinEngine;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.util.Integers;
+
+/**
+ * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags.
+ * <p/>
+ * Parameterised Skein can be used for:
+ * <ul>
+ * <li>MAC generation, by providing a {@link SkeinParameters.Builder#setKey(byte[]) key}.</li>
+ * <li>Randomised hashing, by providing a {@link SkeinParameters.Builder#setNonce(byte[]) nonce}.</li>
+ * <li>A hash function for digital signatures, associating a
+ * {@link SkeinParameters.Builder#setPublicKey(byte[]) public key} with the message digest.</li>
+ * <li>A key derivation function, by providing a
+ * {@link SkeinParameters.Builder#setKeyIdentifier(byte[]) key identifier}.</li>
+ * <li>Personalised hashing, by providing a
+ * {@link SkeinParameters.Builder#setPersonalisation(Date, String, String) recommended format} or
+ * {@link SkeinParameters.Builder#setPersonalisation(byte[]) arbitrary} personalisation string.</li>
+ * </ul>
+ *
+ * @see SkeinEngine
+ * @see SkeinDigest
+ * @see SkeinMac
+ */
+public class SkeinParameters
+ implements CipherParameters
+{
+ /**
+ * The parameter type for a secret key, supporting MAC or KDF functions: {@value
+ * #PARAM_TYPE_KEY}.
+ */
+ public static final int PARAM_TYPE_KEY = 0;
+
+ /**
+ * The parameter type for the Skein configuration block: {@value #PARAM_TYPE_CONFIG}.
+ */
+ public static final int PARAM_TYPE_CONFIG = 4;
+
+ /**
+ * The parameter type for a personalisation string: {@value #PARAM_TYPE_PERSONALISATION}.
+ */
+ public static final int PARAM_TYPE_PERSONALISATION = 8;
+
+ /**
+ * The parameter type for a public key: {@value #PARAM_TYPE_PUBLIC_KEY}.
+ */
+ public static final int PARAM_TYPE_PUBLIC_KEY = 12;
+
+ /**
+ * The parameter type for a key identifier string: {@value #PARAM_TYPE_KEY_IDENTIFIER}.
+ */
+ public static final int PARAM_TYPE_KEY_IDENTIFIER = 16;
+
+ /**
+ * The parameter type for a nonce: {@value #PARAM_TYPE_NONCE}.
+ */
+ public static final int PARAM_TYPE_NONCE = 20;
+
+ /**
+ * The parameter type for the message: {@value #PARAM_TYPE_MESSAGE}.
+ */
+ public static final int PARAM_TYPE_MESSAGE = 48;
+
+ /**
+ * The parameter type for the output transformation: {@value #PARAM_TYPE_OUTPUT}.
+ */
+ public static final int PARAM_TYPE_OUTPUT = 63;
+
+ private Hashtable parameters;
+
+ public SkeinParameters()
+ {
+ this(new Hashtable());
+ }
+
+ private SkeinParameters(final Hashtable parameters)
+ {
+ this.parameters = parameters;
+ }
+
+ /**
+ * Obtains a map of type (Integer) to value (byte[]) for the parameters tracked in this object.
+ */
+ public Hashtable getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY key parameter}, or <code>null</code> if not
+ * set.
+ */
+ public byte[] getKey()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getPersonalisation()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PERSONALISATION));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getPublicKey()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PUBLIC_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getKeyIdentifier()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY_IDENTIFIER));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or <code>null</code> if
+ * not set.
+ */
+ public byte[] getNonce()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_NONCE));
+ }
+
+ /**
+ * A builder for {@link SkeinParameters}.
+ */
+ public static class Builder
+ {
+ private Hashtable parameters = new Hashtable();
+
+ public Builder()
+ {
+ }
+
+ public Builder(Hashtable paramsMap)
+ {
+ Enumeration keys = paramsMap.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, paramsMap.get(key));
+ }
+ }
+
+ public Builder(SkeinParameters params)
+ {
+ Enumeration keys = params.parameters.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, params.parameters.get(key));
+ }
+ }
+
+ /**
+ * Sets a parameters to apply to the Skein hash function.<br>
+ * Parameter types must be in the range 0,5..62, and cannot use the value {@value
+ * SkeinParameters#PARAM_TYPE_MESSAGE} (reserved for message body).
+ * <p/>
+ * Parameters with type < {@value SkeinParameters#PARAM_TYPE_MESSAGE} are processed before
+ * the message content, parameters with type > {@value SkeinParameters#PARAM_TYPE_MESSAGE}
+ * are processed after the message and prior to output.
+ *
+ * @param type the type of the parameter, in the range 5..62.
+ * @param value the byte sequence of the parameter.
+ * @return
+ */
+ public Builder set(int type, byte[] value)
+ {
+ if (value == null)
+ {
+ throw new IllegalArgumentException("Parameter value must not be null.");
+ }
+ if ((type != PARAM_TYPE_KEY)
+ && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
+ {
+ throw new IllegalArgumentException("Parameter types must be in the range 0,5..47,49..62.");
+ }
+ if (type == PARAM_TYPE_CONFIG)
+ {
+ throw new IllegalArgumentException("Parameter type " + PARAM_TYPE_CONFIG
+ + " is reserved for internal use.");
+ }
+ this.parameters.put(Integers.valueOf(type), value);
+ return this;
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY} parameter.
+ */
+ public Builder setKey(byte[] key)
+ {
+ return set(PARAM_TYPE_KEY, key);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_PERSONALISATION} parameter.
+ */
+ public Builder setPersonalisation(byte[] personalisation)
+ {
+ return set(PARAM_TYPE_PERSONALISATION, personalisation);
+ }
+
+ /**
+ * Implements the recommended personalisation format for Skein defined in Section 4.11 of
+ * the Skein 1.3 specification.
+ * <p/>
+ * The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte
+ * sequence using UTF-8 encoding.
+ *
+ * @param date the date the personalised application of the Skein was defined.
+ * @param emailAddress the email address of the creation of the personalised application.
+ * @param distinguisher an arbitrary personalisation string distinguishing the application.
+ * @return
+ */
+ public Builder setPersonalisation(Date date, String emailAddress, String distinguisher)
+ {
+ try
+ {
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ final OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8");
+ final DateFormat format = new SimpleDateFormat("YYYYMMDD");
+ out.write(format.format(date));
+ out.write(" ");
+ out.write(emailAddress);
+ out.write(" ");
+ out.write(distinguisher);
+ out.close();
+ return set(PARAM_TYPE_PERSONALISATION, bout.toByteArray());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Byte I/O failed: " + e);
+ }
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+ */
+ public Builder setPublicKey(byte[] publicKey)
+ {
+ return set(PARAM_TYPE_PUBLIC_KEY, publicKey);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+ */
+ public Builder setKeyIdentifier(byte[] keyIdentifier)
+ {
+ return set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_NONCE} parameter.
+ */
+ public Builder setNonce(byte[] nonce)
+ {
+ return set(PARAM_TYPE_NONCE, nonce);
+ }
+
+ /**
+ * Constructs a new {@link SkeinParameters} instance with the parameters provided to this
+ * builder.
+ */
+ public SkeinParameters build()
+ {
+ return new SkeinParameters(parameters);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java
new file mode 100644
index 0000000..fa16fac
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Parameters for tweakable block ciphers.
+ */
+public class TweakableBlockCipherParameters
+ implements CipherParameters
+{
+ private final byte[] tweak;
+ private final KeyParameter key;
+
+ public TweakableBlockCipherParameters(final KeyParameter key, final byte[] tweak)
+ {
+ this.key = key;
+ this.tweak = Arrays.clone(tweak);
+ }
+
+ /**
+ * Gets the key.
+ *
+ * @return the key to use, or <code>null</code> to use the current key.
+ */
+ public KeyParameter getKey()
+ {
+ return key;
+ }
+
+ /**
+ * Gets the tweak value.
+ *
+ * @return the tweak to use, or <code>null</code> to use the current tweak.
+ */
+ public byte[] getTweak()
+ {
+ return tweak;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html
deleted file mode 100644
index 4e00a75..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for parameter objects for ciphers and generators.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java
index 209b5e2..3245eab 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java
@@ -4,6 +4,9 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
+/**
+ * A secure random that returns pre-seeded data to calls of nextBytes() or generateSeed().
+ */
public class FixedSecureRandom
extends SecureRandom
{
@@ -70,7 +73,16 @@ public class FixedSecureRandom
_index += bytes.length;
}
-
+
+ public byte[] generateSeed(int numBytes)
+ {
+ byte[] bytes = new byte[numBytes];
+
+ this.nextBytes(bytes);
+
+ return bytes;
+ }
+
//
// classpath's implementation of SecureRandom doesn't currently go back to nextBytes
// when next is called. We can't override next as it's a final method.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
index 66f05c5..59ea101 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
@@ -6,6 +6,7 @@ import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG;
+import org.bouncycastle.crypto.prng.drbg.DualECPoints;
import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG;
import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG;
import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG;
@@ -144,7 +145,7 @@ public class SP800SecureRandomBuilder
}
/**
- * Build a SecureRandom based on a SP 800-90A Dual EC DRBG.
+ * Build a SecureRandom based on a SP 800-90A Dual EC DRBG using the NIST point set.
*
* @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
* @param nonce nonce value to use in DRBG construction.
@@ -156,6 +157,21 @@ public class SP800SecureRandomBuilder
return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new DualECDRBGProvider(digest, nonce, personalizationString, securityStrength), predictionResistant);
}
+ /**
+ * Build a SecureRandom based on a SP 800-90A Dual EC DRBG according to a defined point set.
+ *
+ * @param pointSet an array of DualECPoints to use for DRB generation.
+ * @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a Dual EC DRBG.
+ */
+ public SP800SecureRandom buildDualEC(DualECPoints[] pointSet, Digest digest, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new ConfigurableDualECDRBGProvider(pointSet, digest, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+
private static class HashDRBGProvider
implements DRBGProvider
{
@@ -200,6 +216,31 @@ public class SP800SecureRandomBuilder
}
}
+ private static class ConfigurableDualECDRBGProvider
+ implements DRBGProvider
+ {
+ private final DualECPoints[] pointSet;
+ private final Digest digest;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public ConfigurableDualECDRBGProvider(DualECPoints[] pointSet, Digest digest, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.pointSet = new DualECPoints[pointSet.length];
+ System.arraycopy(pointSet, 0, this.pointSet, 0, pointSet.length);
+ this.digest = digest;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new DualECSP800DRBG(pointSet, digest, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+
private static class HMacDRBGProvider
implements DRBGProvider
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java
new file mode 100644
index 0000000..c3715bc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.crypto.prng.drbg;
+
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * General class for providing point pairs for use with DualEC DRBG. See NIST SP 800-90A for further details.
+ */
+public class DualECPoints
+{
+ private final ECPoint p;
+ private final ECPoint q;
+ private final int securityStrength;
+ private final int cofactor;
+
+ /**
+ * Base Constructor.
+ * <p>
+ * The cofactor is used to calculate the output block length (maxOutlen) according to
+ * <pre>
+ * max_outlen = largest multiple of 8 less than ((field size in bits) - (13 + log2(cofactor))
+ * </pre>
+ * </p>
+ * @param securityStrength maximum security strength to be associated with these parameters
+ * @param p the P point.
+ * @param q the Q point.
+ * @param cofactor cofactor associated with the domain parameters for the point generation.
+ */
+ public DualECPoints(int securityStrength, ECPoint p, ECPoint q, int cofactor)
+ {
+ if (!p.getCurve().equals(q.getCurve()))
+ {
+ throw new IllegalArgumentException("points need to be on the same curve");
+ }
+
+ this.securityStrength = securityStrength;
+ this.p = p;
+ this.q = q;
+ this.cofactor = cofactor;
+ }
+
+ public int getSeedLen()
+ {
+ return p.getCurve().getFieldSize();
+ }
+
+ public int getMaxOutlen()
+ {
+ return ((p.getCurve().getFieldSize() - (13 + log2(cofactor))) / 8) * 8;
+ }
+
+ public ECPoint getP()
+ {
+ return p;
+ }
+
+ public ECPoint getQ()
+ {
+ return q;
+ }
+
+ public int getSecurityStrength()
+ {
+ return securityStrength;
+ }
+
+ public int getCofactor()
+ {
+ return cofactor;
+ }
+
+ private static int log2(int value)
+ {
+ int log = 0;
+
+ while ((value >>= 1) != 0)
+ {
+ log++;
+ }
+
+ return log;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java
index 3cee39c..8d326ff 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java
@@ -6,7 +6,6 @@ import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.prng.EntropySource;
import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
@@ -35,6 +34,26 @@ public class DualECSP800DRBG
private static final BigInteger p521_Qx = new BigInteger("1b9fa3e518d683c6b65763694ac8efbaec6fab44f2276171a42726507dd08add4c3b3f4c1ebc5b1222ddba077f722943b24c3edfa0f85fe24d0c8c01591f0be6f63", 16);
private static final BigInteger p521_Qy = new BigInteger("1f3bdba585295d9a1110d1df1f9430ef8442c5018976ff3437ef91b81dc0b8132c8d5c39c32d0e004a3092b7d327c0e7a4d26d2c7b69b58f9066652911e457779de", 16);
+ private static final DualECPoints[] nistPoints;
+
+ static
+ {
+ nistPoints = new DualECPoints[3];
+
+ ECCurve.Fp curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve();
+
+ nistPoints[0] = new DualECPoints(128, curve.createPoint(p256_Px, p256_Py), curve.createPoint(p256_Qx, p256_Qy), 1);
+
+ curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve();
+
+ nistPoints[1] = new DualECPoints(192, curve.createPoint(p384_Px, p384_Py), curve.createPoint(p384_Qx, p384_Qy), 1);
+
+ curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve();
+
+ nistPoints[2] = new DualECPoints(256, curve.createPoint(p521_Px, p521_Py), curve.createPoint(p521_Qx, p521_Qy), 1);
+ }
+
+
private static final long RESEED_MAX = 1L << (32 - 1);
private static final int MAX_ADDITIONAL_INPUT = 1 << (13 - 1);
private static final int MAX_ENTROPY_LENGTH = 1 << (13 - 1);
@@ -65,6 +84,23 @@ public class DualECSP800DRBG
*/
public DualECSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
{
+ this(nistPoints, digest, securityStrength, entropySource, personalizationString, nonce);
+ }
+
+ /**
+ * Construct a SP800-90A Dual EC DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param pointSet an array of points to choose from, in order of increasing security strength
+ * @param digest source digest to use with the DRB stream.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public DualECSP800DRBG(DualECPoints[] pointSet, Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
_digest = digest;
_entropySource = entropySource;
_securityStrength = securityStrength;
@@ -82,43 +118,23 @@ public class DualECSP800DRBG
byte[] entropy = entropySource.getEntropy();
byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString);
- if (securityStrength <= 128)
- {
- if (Utils.getMaxSecurityStrength(digest) < 128)
- {
- throw new IllegalArgumentException("Requested security strength is not supported by digest");
- }
- _seedlen = 256;
- _outlen = 240 / 8;
- _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve();
- _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Px), new ECFieldElement.Fp(_curve.getQ(), p256_Py));
- _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Qx), new ECFieldElement.Fp(_curve.getQ(), p256_Qy));
- }
- else if (securityStrength <= 192)
+ for (int i = 0; i != pointSet.length; i++)
{
- if (Utils.getMaxSecurityStrength(digest) < 192)
+ if (securityStrength <= pointSet[i].getSecurityStrength())
{
- throw new IllegalArgumentException("Requested security strength is not supported by digest");
+ if (Utils.getMaxSecurityStrength(digest) < pointSet[i].getSecurityStrength())
+ {
+ throw new IllegalArgumentException("Requested security strength is not supported by digest");
+ }
+ _seedlen = pointSet[i].getSeedLen();
+ _outlen = pointSet[i].getMaxOutlen() / 8;
+ _P = pointSet[i].getP();
+ _Q = pointSet[i].getQ();
+ break;
}
- _seedlen = 384;
- _outlen = 368 / 8;
- _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve();
- _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Px), new ECFieldElement.Fp(_curve.getQ(), p384_Py));
- _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Qx), new ECFieldElement.Fp(_curve.getQ(), p384_Qy));
}
- else if (securityStrength <= 256)
- {
- if (Utils.getMaxSecurityStrength(digest) < 256)
- {
- throw new IllegalArgumentException("Requested security strength is not supported by digest");
- }
- _seedlen = 521;
- _outlen = 504 / 8;
- _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve();
- _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Px), new ECFieldElement.Fp(_curve.getQ(), p521_Py));
- _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Qx), new ECFieldElement.Fp(_curve.getQ(), p521_Qy));
- }
- else
+
+ if (_P == null)
{
throw new IllegalArgumentException("security strength cannot be greater than 256 bits");
}
@@ -159,50 +175,69 @@ public class DualECSP800DRBG
additionalInput = null;
}
+ BigInteger s;
+
if (additionalInput != null)
{
// Note: we ignore the use of pad8 on the additional input as we mandate byte arrays for it.
additionalInput = Utils.hash_df(_digest, additionalInput, _seedlen);
+ s = new BigInteger(1, xor(_s, additionalInput));
}
+ else
+ {
+ s = new BigInteger(1, _s);
+ }
+
+ // make sure we start with a clean output array.
+ Arrays.fill(output, (byte)0);
+
+ int outOffset = 0;
for (int i = 0; i < m; i++)
{
- BigInteger t = new BigInteger(1, xor(_s, additionalInput));
-
- _s = _P.multiply(t).getX().toBigInteger().toByteArray();
+ s = getScalarMultipleXCoord(_P, s);
//System.err.println("S: " + new String(Hex.encode(_s)));
- byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray();
+ byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray();
if (r.length > _outlen)
{
- System.arraycopy(r, r.length - _outlen, output, i * _outlen, _outlen);
+ System.arraycopy(r, r.length - _outlen, output, outOffset, _outlen);
}
else
{
- System.arraycopy(r, 0, output, i * _outlen + (_outlen - r.length), r.length);
+ System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), r.length);
}
//System.err.println("R: " + new String(Hex.encode(r)));
- additionalInput = null;
+ outOffset += _outlen;
_reseedCounter++;
}
- if (m * _outlen < output.length)
+ if (outOffset < output.length)
{
- BigInteger t = new BigInteger(1, xor(_s, additionalInput));
+ s = getScalarMultipleXCoord(_P, s);
- _s = _P.multiply(t).getX().toBigInteger().toByteArray();
+ byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray();
- byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray();
+ int required = output.length - outOffset;
- System.arraycopy(r, 0, output, m * _outlen, output.length - (m * _outlen));
+ if (r.length > _outlen)
+ {
+ System.arraycopy(r, r.length - _outlen, output, outOffset, required);
+ }
+ else
+ {
+ System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), required);
+ }
+
+ _reseedCounter++;
}
// Need to preserve length of S as unsigned int.
- _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(new BigInteger(1, _s)).getX().toBigInteger());
+ _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(s).normalize().getAffineXCoord().toBigInteger());
return numberOfBits;
}
@@ -264,4 +299,9 @@ public class DualECSP800DRBG
return s;
}
+
+ private BigInteger getScalarMultipleXCoord(ECPoint p, BigInteger s)
+ {
+ return p.multiply(s).normalize().getAffineXCoord().toBigInteger();
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html
deleted file mode 100644
index 630809b..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-NIST Deterministic Random Bit Generators (SP 800-90A).
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html
deleted file mode 100644
index 9ad3854..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Lightweight psuedo-random number generators and SecureRandom variants and builders.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java
new file mode 100644
index 0000000..fced06e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * Interface define calculators of K values for DSA/ECDSA.
+ */
+public interface DSAKCalculator
+{
+ /**
+ * Return true if this calculator is deterministic, false otherwise.
+ *
+ * @return true if deterministic, otherwise false.
+ */
+ boolean isDeterministic();
+
+ /**
+ * Non-deterministic initialiser.
+ *
+ * @param n the order of the DSA group.
+ * @param random a source of randomness.
+ */
+ void init(BigInteger n, SecureRandom random);
+
+ /**
+ * Deterministic initialiser.
+ *
+ * @param n the order of the DSA group.
+ * @param d the DSA private value.
+ * @param message the message being signed.
+ */
+ void init(BigInteger n, BigInteger d, byte[] message);
+
+ /**
+ * Return the next valid value of K.
+ *
+ * @return a K value.
+ */
+ BigInteger nextK();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
index a96cef0..292c408 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
@@ -1,5 +1,8 @@
package org.bouncycastle.crypto.signers;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DSA;
import org.bouncycastle.crypto.params.DSAKeyParameters;
@@ -8,9 +11,6 @@ import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
/**
* The Digital Signature Algorithm - as described in "Handbook of Applied
* Cryptography", pages 452 - 453.
@@ -18,9 +18,28 @@ import java.security.SecureRandom;
public class DSASigner
implements DSA
{
- DSAKeyParameters key;
+ private final DSAKCalculator kCalculator;
- SecureRandom random;
+ private DSAKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * Default configuration, random K values.
+ */
+ public DSASigner()
+ {
+ this.kCalculator = new RandomDSAKCalculator();
+ }
+
+ /**
+ * Configuration with an alternate, possibly deterministic calculator of K.
+ *
+ * @param kCalculator a K value calculator.
+ */
+ public DSASigner(DSAKCalculator kCalculator)
+ {
+ this.kCalculator = kCalculator;
+ }
public void init(
boolean forSigning,
@@ -59,14 +78,17 @@ public class DSASigner
{
DSAParameters params = key.getParameters();
BigInteger m = calculateE(params.getQ(), message);
- BigInteger k;
- int qBitLength = params.getQ().bitLength();
- do
+ if (kCalculator.isDeterministic())
{
- k = new BigInteger(qBitLength, random);
+ kCalculator.init(params.getQ(), ((DSAPrivateKeyParameters)key).getX(), message);
}
- while (k.compareTo(params.getQ()) >= 0);
+ else
+ {
+ kCalculator.init(params.getQ(), random);
+ }
+
+ BigInteger k = kCalculator.nextK();
BigInteger r = params.getG().modPow(k, params.getP()).mod(params.getQ());
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
index a8fc194..0e76950 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
@@ -5,6 +5,7 @@ import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
@@ -56,12 +57,17 @@ public class DSTU4145Signer
public BigInteger[] generateSignature(byte[] message)
{
- ECFieldElement h = hash2FieldElement(key.getParameters().getCurve(), message);
- if (h.toBigInteger().signum() == 0)
+ ECDomainParameters parameters = key.getParameters();
+
+ ECCurve curve = parameters.getCurve();
+
+ ECFieldElement h = hash2FieldElement(curve, message);
+ if (h.isZero())
{
- h = key.getParameters().getCurve().fromBigInteger(ONE);
+ h = curve.fromBigInteger(ONE);
}
+ BigInteger n = parameters.getN();
BigInteger e, r, s;
ECFieldElement Fe, y;
@@ -71,17 +77,17 @@ public class DSTU4145Signer
{
do
{
- e = generateRandomInteger(key.getParameters().getN(), random);
- Fe = key.getParameters().getG().multiply(e).getX();
+ e = generateRandomInteger(n, random);
+ Fe = parameters.getG().multiply(e).normalize().getAffineXCoord();
}
- while (Fe.toBigInteger().signum() == 0);
+ while (Fe.isZero());
y = h.multiply(Fe);
- r = fieldElement2Integer(key.getParameters().getN(), y);
+ r = fieldElement2Integer(n, y);
}
while (r.signum() == 0);
- s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(key.getParameters().getN());
+ s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(n);
}
while (s.signum() == 0);
@@ -90,22 +96,28 @@ public class DSTU4145Signer
public boolean verifySignature(byte[] message, BigInteger r, BigInteger s)
{
- if (r.signum() == 0 || s.signum() == 0)
+ if (r.signum() <= 0 || s.signum() <= 0)
{
return false;
}
- if (r.compareTo(key.getParameters().getN()) >= 0 || s.compareTo(key.getParameters().getN()) >= 0)
+
+ ECDomainParameters parameters = key.getParameters();
+
+ BigInteger n = parameters.getN();
+ if (r.compareTo(n) >= 0 || s.compareTo(n) >= 0)
{
return false;
}
- ECFieldElement h = hash2FieldElement(key.getParameters().getCurve(), message);
- if (h.toBigInteger().signum() == 0)
+ ECCurve curve = parameters.getCurve();
+
+ ECFieldElement h = hash2FieldElement(curve, message);
+ if (h.isZero())
{
- h = key.getParameters().getCurve().fromBigInteger(ONE);
+ h = curve.fromBigInteger(ONE);
}
- ECPoint R = ECAlgorithms.sumOfTwoMultiplies(key.getParameters().getG(), s, ((ECPublicKeyParameters)key).getQ(), r);
+ ECPoint R = ECAlgorithms.sumOfTwoMultiplies(parameters.getG(), s, ((ECPublicKeyParameters)key).getQ(), r).normalize();
// components must be bogus.
if (R.isInfinity())
@@ -113,8 +125,8 @@ public class DSTU4145Signer
return false;
}
- ECFieldElement y = h.multiply(R.getX());
- return fieldElement2Integer(key.getParameters().getN(), y).compareTo(r) == 0;
+ ECFieldElement y = h.multiply(R.getAffineXCoord());
+ return fieldElement2Integer(n, y).compareTo(r) == 0;
}
/**
@@ -142,7 +154,7 @@ public class DSTU4145Signer
byte[] data = Arrays.clone(hash);
reverseBytes(data);
BigInteger num = new BigInteger(1, data);
- while (num.bitLength() >= curve.getFieldSize())
+ while (num.bitLength() > curve.getFieldSize())
{
num = num.clearBit(num.bitLength() - 1);
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
index a80c574..2a1f98e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
@@ -19,9 +19,28 @@ import org.bouncycastle.math.ec.ECPoint;
public class ECDSASigner
implements ECConstants, DSA
{
- ECKeyParameters key;
+ private final DSAKCalculator kCalculator;
- SecureRandom random;
+ private ECKeyParameters key;
+ private SecureRandom random;
+
+ /**
+ * Default configuration, random K values.
+ */
+ public ECDSASigner()
+ {
+ this.kCalculator = new RandomDSAKCalculator();
+ }
+
+ /**
+ * Configuration with an alternate, possibly deterministic calculator of K.
+ *
+ * @param kCalculator a K value calculator.
+ */
+ public ECDSASigner(DSAKCalculator kCalculator)
+ {
+ this.kCalculator = kCalculator;
+ }
public void init(
boolean forSigning,
@@ -64,24 +83,28 @@ public class ECDSASigner
BigInteger r = null;
BigInteger s = null;
+ if (kCalculator.isDeterministic())
+ {
+ kCalculator.init(n, ((ECPrivateKeyParameters)key).getD(), message);
+ }
+ else
+ {
+ kCalculator.init(n, random);
+ }
+
// 5.3.2
do // generate s
{
BigInteger k = null;
- int nBitLength = n.bitLength();
do // generate r
{
- do
- {
- k = new BigInteger(nBitLength, random);
- }
- while (k.equals(ZERO) || k.compareTo(n) >= 0);
+ k = kCalculator.nextK();
- ECPoint p = key.getParameters().getG().multiply(k);
+ ECPoint p = key.getParameters().getG().multiply(k).normalize();
// 5.3.3
- BigInteger x = p.getX().toBigInteger();
+ BigInteger x = p.getAffineXCoord().toBigInteger();
r = x.mod(n);
}
@@ -135,7 +158,7 @@ public class ECDSASigner
ECPoint G = key.getParameters().getG();
ECPoint Q = ((ECPublicKeyParameters)key).getQ();
- ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2);
+ ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2).normalize();
// components must be bogus.
if (point.isInfinity())
@@ -143,7 +166,7 @@ public class ECDSASigner
return false;
}
- BigInteger v = point.getX().toBigInteger().mod(n);
+ BigInteger v = point.getAffineXCoord().toBigInteger().mod(n);
return v.equals(r);
}
@@ -153,17 +176,11 @@ public class ECDSASigner
int log2n = n.bitLength();
int messageBitLength = message.length * 8;
- if (log2n >= messageBitLength)
+ BigInteger e = new BigInteger(1, message);
+ if (log2n < messageBitLength)
{
- return new BigInteger(1, message);
- }
- else
- {
- BigInteger trunc = new BigInteger(1, message);
-
- trunc = trunc.shiftRight(messageBitLength - log2n);
-
- return trunc;
+ e = e.shiftRight(messageBitLength - log2n);
}
+ return e;
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
index 7256d35..f6d7f4f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
@@ -82,9 +82,9 @@ public class ECGOST3410Signer
}
while (k.equals(ECConstants.ZERO));
- ECPoint p = key.getParameters().getG().multiply(k);
+ ECPoint p = key.getParameters().getG().multiply(k).normalize();
- BigInteger x = p.getX().toBigInteger();
+ BigInteger x = p.getAffineXCoord().toBigInteger();
r = x.mod(n);
}
@@ -143,7 +143,7 @@ public class ECGOST3410Signer
ECPoint G = key.getParameters().getG(); // P
ECPoint Q = ((ECPublicKeyParameters)key).getQ();
- ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, z1, Q, z2);
+ ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, z1, Q, z2).normalize();
// components must be bogus.
if (point.isInfinity())
@@ -151,7 +151,7 @@ public class ECGOST3410Signer
return false;
}
- BigInteger R = point.getX().toBigInteger().mod(n);
+ BigInteger R = point.getAffineXCoord().toBigInteger().mod(n);
return R.equals(r);
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
index 07e8ca7..72bbbcb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
@@ -101,8 +101,8 @@ public class ECNRSigner
// BigInteger Vx = tempPair.getPublic().getW().getAffineX();
ECPublicKeyParameters V = (ECPublicKeyParameters)tempPair.getPublic(); // get temp's public key
- BigInteger Vx = V.getQ().getX().toBigInteger(); // get the point's x coordinate
-
+ BigInteger Vx = V.getQ().normalize().getAffineXCoord().toBigInteger(); // get the point's x coordinate
+
r = Vx.add(e).mod(n);
}
while (r.equals(ECConstants.ZERO));
@@ -172,7 +172,7 @@ public class ECNRSigner
ECPoint G = pubKey.getParameters().getG();
ECPoint W = pubKey.getQ();
// calculate P using Bouncy math
- ECPoint P = ECAlgorithms.sumOfTwoMultiplies(G, s, W, r);
+ ECPoint P = ECAlgorithms.sumOfTwoMultiplies(G, s, W, r).normalize();
// components must be bogus.
if (P.isInfinity())
@@ -180,7 +180,7 @@ public class ECNRSigner
return false;
}
- BigInteger x = P.getX().toBigInteger();
+ BigInteger x = P.getAffineXCoord().toBigInteger();
BigInteger t = r.subtract(x).mod(n);
return t.equals(e);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
new file mode 100644
index 0000000..b96e3f3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
@@ -0,0 +1,161 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * A deterministic K calculator based on the algorithm in section 3.2 of RFC 6979.
+ */
+public class HMacDSAKCalculator
+ implements DSAKCalculator
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private final HMac hMac;
+ private final byte[] K;
+ private final byte[] V;
+
+ private BigInteger n;
+
+ /**
+ * Base constructor.
+ *
+ * @param digest digest to build the HMAC on.
+ */
+ public HMacDSAKCalculator(Digest digest)
+ {
+ this.hMac = new HMac(digest);
+ this.V = new byte[hMac.getMacSize()];
+ this.K = new byte[hMac.getMacSize()];
+ }
+
+ public boolean isDeterministic()
+ {
+ return true;
+ }
+
+ public void init(BigInteger n, SecureRandom random)
+ {
+ throw new IllegalStateException("Operation not supported");
+ }
+
+ public void init(BigInteger n, BigInteger d, byte[] message)
+ {
+ this.n = n;
+
+ Arrays.fill(V, (byte)0x01);
+ Arrays.fill(K, (byte)0);
+
+ byte[] x = new byte[(n.bitLength() + 7) / 8];
+ byte[] dVal = BigIntegers.asUnsignedByteArray(d);
+
+ System.arraycopy(dVal, 0, x, x.length - dVal.length, dVal.length);
+
+ byte[] m = new byte[(n.bitLength() + 7) / 8];
+
+ BigInteger mInt = bitsToInt(message);
+
+ if (mInt.compareTo(n) > 0)
+ {
+ mInt = mInt.subtract(n);
+ }
+
+ byte[] mVal = BigIntegers.asUnsignedByteArray(mInt);
+
+ System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length);
+
+ hMac.init(new KeyParameter(K));
+
+ hMac.update(V, 0, V.length);
+ hMac.update((byte)0x00);
+ hMac.update(x, 0, x.length);
+ hMac.update(m, 0, m.length);
+
+ hMac.doFinal(K, 0);
+
+ hMac.init(new KeyParameter(K));
+
+ hMac.update(V, 0, V.length);
+
+ hMac.doFinal(V, 0);
+
+ hMac.update(V, 0, V.length);
+ hMac.update((byte)0x01);
+ hMac.update(x, 0, x.length);
+ hMac.update(m, 0, m.length);
+
+ hMac.doFinal(K, 0);
+
+ hMac.init(new KeyParameter(K));
+
+ hMac.update(V, 0, V.length);
+
+ hMac.doFinal(V, 0);
+ }
+
+ public BigInteger nextK()
+ {
+ byte[] t = new byte[((n.bitLength() + 7) / 8)];
+
+ for (;;)
+ {
+ int tOff = 0;
+
+ while (tOff < t.length)
+ {
+ hMac.update(V, 0, V.length);
+
+ hMac.doFinal(V, 0);
+
+ if (t.length - tOff < V.length)
+ {
+ System.arraycopy(V, 0, t, tOff, t.length - tOff);
+ tOff += t.length - tOff;
+ }
+ else
+ {
+ System.arraycopy(V, 0, t, tOff, V.length);
+ tOff += V.length;
+ }
+ }
+
+ BigInteger k = bitsToInt(t);
+
+ if (k.equals(ZERO) || k.compareTo(n) >= 0)
+ {
+ hMac.update(V, 0, V.length);
+ hMac.update((byte)0x00);
+
+ hMac.doFinal(K, 0);
+
+ hMac.init(new KeyParameter(K));
+
+ hMac.update(V, 0, V.length);
+
+ hMac.doFinal(V, 0);
+ }
+ else
+ {
+ return k;
+ }
+ }
+ }
+
+ private BigInteger bitsToInt(byte[] t)
+ {
+ BigInteger v = new BigInteger(1, t);
+
+ if (t.length * 8 > n.bitLength())
+ {
+ v = v.shiftRight(t.length * 8 - n.bitLength());
+ }
+
+ return v;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
index f33ed31..aaae064 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
@@ -57,9 +57,15 @@ public class RSADigestSigner
public RSADigestSigner(
Digest digest)
{
- this.digest = digest;
+ this(digest, (ASN1ObjectIdentifier)oidMap.get(digest.getAlgorithmName()));
+ }
- algId = new AlgorithmIdentifier((ASN1ObjectIdentifier)oidMap.get(digest.getAlgorithmName()), DERNull.INSTANCE);
+ public RSADigestSigner(
+ Digest digest,
+ ASN1ObjectIdentifier digestOid)
+ {
+ this.digest = digest;
+ this.algId = new AlgorithmIdentifier(digestOid, DERNull.INSTANCE);
}
/**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
new file mode 100644
index 0000000..bbd8cda
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+class RandomDSAKCalculator
+ implements DSAKCalculator
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private BigInteger q;
+ private SecureRandom random;
+
+ public boolean isDeterministic()
+ {
+ return false;
+ }
+
+ public void init(BigInteger n, SecureRandom random)
+ {
+ this.q = n;
+ this.random = random;
+ }
+
+ public void init(BigInteger n, BigInteger d, byte[] message)
+ {
+ throw new IllegalStateException("Operation not supported");
+ }
+
+ public BigInteger nextK()
+ {
+ int qBitLength = q.bitLength();
+
+ BigInteger k;
+ do
+ {
+ k = new BigInteger(qBitLength, random);
+ }
+ while (k.equals(ZERO) || k.compareTo(q) >= 0);
+
+ return k;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html
deleted file mode 100644
index 151d3d5..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Basic signers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java
new file mode 100644
index 0000000..ef7f4fb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsAgreementCredentials
+ extends AbstractTlsCredentials
+ implements TlsAgreementCredentials
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java
index 9c2a526..71a2cab 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java
@@ -5,11 +5,9 @@ import java.io.IOException;
public class AbstractTlsCipherFactory
implements TlsCipherFactory
{
-
public TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
throws IOException
{
-
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
index 9e113f9..7d4fd03 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
@@ -8,12 +8,13 @@ public abstract class AbstractTlsClient
extends AbstractTlsPeer
implements TlsClient
{
-
protected TlsCipherFactory cipherFactory;
protected TlsClientContext context;
protected Vector supportedSignatureAlgorithms;
+ protected int[] namedCurves;
+ protected short[] clientECPointFormats, serverECPointFormats;
protected int selectedCipherSuite;
protected short selectedCompressionMethod;
@@ -33,6 +34,11 @@ public abstract class AbstractTlsClient
this.context = context;
}
+ public TlsSession getSessionToResume()
+ {
+ return null;
+ }
+
/**
* RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value
* {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest
@@ -46,7 +52,7 @@ public abstract class AbstractTlsClient
// return ProtocolVersion.SSLv3;
// "the lowest version number supported by the client"
- // return getMinimumServerVersion();
+ // return getMinimumVersion();
// "the value of ClientHello.client_version"
return getClientVersion();
@@ -54,13 +60,12 @@ public abstract class AbstractTlsClient
public ProtocolVersion getClientVersion()
{
- return ProtocolVersion.TLSv11;
+ return ProtocolVersion.TLSv12;
}
public Hashtable getClientExtensions()
throws IOException
{
-
Hashtable clientExtensions = null;
ProtocolVersion clientVersion = context.getClientVersion();
@@ -71,14 +76,13 @@ public abstract class AbstractTlsClient
*/
if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
{
-
// TODO Provide a way for the user to specify the acceptable hash/signature algorithms.
- short[] hashAlgorithms = new short[]{HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
- HashAlgorithm.sha224, HashAlgorithm.sha1};
+ short[] hashAlgorithms = new short[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
+ HashAlgorithm.sha224, HashAlgorithm.sha1 };
// TODO Sort out ECDSA signatures and add them as the preferred option here
- short[] signatureAlgorithms = new short[]{SignatureAlgorithm.rsa};
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa };
this.supportedSignatureAlgorithms = new Vector();
for (int i = 0; i < hashAlgorithms.length; ++i)
@@ -96,14 +100,33 @@ public abstract class AbstractTlsClient
this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
SignatureAlgorithm.dsa));
- if (clientExtensions == null)
- {
- clientExtensions = new Hashtable();
- }
+ clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions);
TlsUtils.addSignatureAlgorithmsExtension(clientExtensions, supportedSignatureAlgorithms);
}
+ if (TlsECCUtils.containsECCCipherSuites(getCipherSuites()))
+ {
+ /*
+ * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message
+ * appends these extensions (along with any others), enumerating the curves it supports
+ * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic
+ * Curves Extension and the Supported Point Formats Extension.
+ */
+ /*
+ * TODO Could just add all the curves since we support them all, but users may not want
+ * to use unnecessarily large fields. Need configuration options.
+ */
+ this.namedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 };
+ this.clientECPointFormats = new short[]{ ECPointFormat.uncompressed,
+ ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
+
+ clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions);
+
+ TlsECCUtils.addSupportedEllipticCurvesExtension(clientExtensions, namedCurves);
+ TlsECCUtils.addSupportedPointFormatsExtension(clientExtensions, clientECPointFormats);
+ }
+
return clientExtensions;
}
@@ -141,19 +164,6 @@ public abstract class AbstractTlsClient
this.selectedCompressionMethod = selectedCompressionMethod;
}
- public void notifySecureRenegotiation(boolean secureRenegotiation)
- throws IOException
- {
- if (!secureRenegotiation)
- {
- /*
- * RFC 5746 3.4. In this case, some clients may want to terminate the handshake instead
- * of continuing; see Section 4.1 for discussion.
- */
- // throw new TlsFatalAlert(AlertDescription.handshake_failure);
- }
- }
-
public void processServerExtensions(Hashtable serverExtensions)
throws IOException
{
@@ -170,6 +180,18 @@ public abstract class AbstractTlsClient
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
+
+ int[] namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(serverExtensions);
+ if (namedCurves != null)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions);
+ if (this.serverECPointFormats != null && !TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
}
}
@@ -210,9 +232,4 @@ public abstract class AbstractTlsClient
throws IOException
{
}
-
- public void notifyHandshakeComplete()
- throws IOException
- {
- }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
index 1ff67e3..5e02892 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
@@ -5,12 +5,12 @@ import java.security.SecureRandom;
abstract class AbstractTlsContext
implements TlsContext
{
-
private SecureRandom secureRandom;
private SecurityParameters securityParameters;
private ProtocolVersion clientVersion = null;
private ProtocolVersion serverVersion = null;
+ private TlsSession session = null;
private Object userObject = null;
AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters)
@@ -34,7 +34,7 @@ abstract class AbstractTlsContext
return clientVersion;
}
- public void setClientVersion(ProtocolVersion clientVersion)
+ void setClientVersion(ProtocolVersion clientVersion)
{
this.clientVersion = clientVersion;
}
@@ -44,11 +44,21 @@ abstract class AbstractTlsContext
return serverVersion;
}
- public void setServerVersion(ProtocolVersion serverVersion)
+ void setServerVersion(ProtocolVersion serverVersion)
{
this.serverVersion = serverVersion;
}
+ public TlsSession getResumableSession()
+ {
+ return session;
+ }
+
+ void setResumableSession(TlsSession session)
+ {
+ this.session = session;
+ }
+
public Object getUserObject()
{
return userObject;
@@ -61,6 +71,10 @@ abstract class AbstractTlsContext
public byte[] exportKeyingMaterial(String asciiLabel, byte[] context_value, int length)
{
+ if (context_value != null && !TlsUtils.isValidUint16(context_value.length))
+ {
+ throw new IllegalArgumentException("'context_value' must have length less than 2^16 (or be null)");
+ }
SecurityParameters sp = getSecurityParameters();
byte[] cr = sp.getClientRandom(), sr = sp.getServerRandom();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java
new file mode 100644
index 0000000..b98743f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsCredentials
+ implements TlsCredentials
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java
new file mode 100644
index 0000000..e6ff39b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsEncryptionCredentials
+ extends AbstractTlsCredentials
+ implements TlsEncryptionCredentials
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java
index 85057c1..43e80e6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java
@@ -7,7 +7,6 @@ import java.util.Vector;
public abstract class AbstractTlsKeyExchange
implements TlsKeyExchange
{
-
protected int keyExchange;
protected Vector supportedSignatureAlgorithms;
@@ -27,7 +26,6 @@ public abstract class AbstractTlsKeyExchange
if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
{
-
/*
* RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension,
* the server MUST do the following:
@@ -45,7 +43,6 @@ public abstract class AbstractTlsKeyExchange
{
switch (keyExchange)
{
-
case KeyExchangeAlgorithm.DH_DSS:
case KeyExchangeAlgorithm.DHE_DSS:
case KeyExchangeAlgorithm.SRP_DSS:
@@ -73,6 +70,12 @@ public abstract class AbstractTlsKeyExchange
break;
}
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ case KeyExchangeAlgorithm.PSK:
+ case KeyExchangeAlgorithm.SRP:
+ break;
+
default:
throw new IllegalStateException("unsupported key exchange algorithm");
}
@@ -88,7 +91,6 @@ public abstract class AbstractTlsKeyExchange
public void processServerCertificate(Certificate serverCertificate)
throws IOException
{
-
if (supportedSignatureAlgorithms == null)
{
/*
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
index bdfd0d5..80d6af7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
@@ -1,8 +1,21 @@
package org.bouncycastle.crypto.tls;
+import java.io.IOException;
+
public abstract class AbstractTlsPeer
implements TlsPeer
{
+ public void notifySecureRenegotiation(boolean secureRenegotiation) throws IOException
+ {
+ if (!secureRenegotiation)
+ {
+ /*
+ * RFC 5746 3.4/3.6. In this case, some clients/servers may want to terminate the handshake instead
+ * of continuing; see Section 4.1/4.3 for discussion.
+ */
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause)
{
@@ -11,4 +24,8 @@ public abstract class AbstractTlsPeer
public void notifyAlertReceived(short alertLevel, short alertDescription)
{
}
+
+ public void notifyHandshakeComplete() throws IOException
+ {
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
index 8235fd1..bd428a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
@@ -4,11 +4,12 @@ import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
+import org.bouncycastle.util.Arrays;
+
public abstract class AbstractTlsServer
extends AbstractTlsPeer
implements TlsServer
{
-
protected TlsCipherFactory cipherFactory;
protected TlsServerContext context;
@@ -18,6 +19,8 @@ public abstract class AbstractTlsServer
protected short[] offeredCompressionMethods;
protected Hashtable clientExtensions;
+ protected short maxFragmentLengthOffered;
+ protected boolean truncatedHMacOffered;
protected Vector supportedSignatureAlgorithms;
protected boolean eccCipherSuitesOffered;
protected int[] namedCurves;
@@ -38,6 +41,16 @@ public abstract class AbstractTlsServer
this.cipherFactory = cipherFactory;
}
+ protected boolean allowTruncatedHMac()
+ {
+ return false;
+ }
+
+ protected Hashtable checkServerExtensions()
+ {
+ return this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.serverExtensions);
+ }
+
protected abstract int[] getCipherSuites();
protected short[] getCompressionMethods()
@@ -57,7 +70,6 @@ public abstract class AbstractTlsServer
protected boolean supportsClientECCCapabilities(int[] namedCurves, short[] ecPointFormats)
{
-
// NOTE: BC supports all the current set of point formats so we don't check them here
if (namedCurves == null)
@@ -106,27 +118,15 @@ public abstract class AbstractTlsServer
this.offeredCompressionMethods = offeredCompressionMethods;
}
- public void notifySecureRenegotiation(boolean secureRenegotiation)
- throws IOException
- {
- if (!secureRenegotiation)
- {
- /*
- * RFC 5746 3.6. In this case, some servers may want to terminate the handshake instead
- * of continuing; see Section 4.3 for discussion.
- */
- throw new TlsFatalAlert(AlertDescription.handshake_failure);
- }
- }
-
public void processClientExtensions(Hashtable clientExtensions)
throws IOException
{
-
this.clientExtensions = clientExtensions;
if (clientExtensions != null)
{
+ this.maxFragmentLengthOffered = TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions);
+ this.truncatedHMacOffered = TlsExtensionsUtils.hasTruncatedHMacExtension(clientExtensions);
this.supportedSignatureAlgorithms = TlsUtils.getSignatureAlgorithmsExtension(clientExtensions);
if (this.supportedSignatureAlgorithms != null)
@@ -176,7 +176,6 @@ public abstract class AbstractTlsServer
public int getSelectedCipherSuite()
throws IOException
{
-
/*
* TODO RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate
* cipher suites against the "signature_algorithms" extension before selecting them. This is
@@ -197,7 +196,9 @@ public abstract class AbstractTlsServer
for (int i = 0; i < cipherSuites.length; ++i)
{
int cipherSuite = cipherSuites[i];
- if (TlsProtocol.arrayContains(this.offeredCipherSuites, cipherSuite)
+
+ // TODO Certain cipher suites may only be available starting at a particular version
+ if (Arrays.contains(this.offeredCipherSuites, cipherSuite)
&& (eccCipherSuitesEnabled || !TlsECCUtils.isECCCipherSuite(cipherSuite)))
{
return this.selectedCipherSuite = cipherSuite;
@@ -212,7 +213,7 @@ public abstract class AbstractTlsServer
short[] compressionMethods = getCompressionMethods();
for (int i = 0; i < compressionMethods.length; ++i)
{
- if (TlsProtocol.arrayContains(offeredCompressionMethods, compressionMethods[i]))
+ if (Arrays.contains(offeredCompressionMethods, compressionMethods[i]))
{
return this.selectedCompressionMethod = compressionMethods[i];
}
@@ -224,6 +225,15 @@ public abstract class AbstractTlsServer
public Hashtable getServerExtensions()
throws IOException
{
+ if (this.maxFragmentLengthOffered >= 0)
+ {
+ TlsExtensionsUtils.addMaxFragmentLengthExtension(checkServerExtensions(), this.maxFragmentLengthOffered);
+ }
+
+ if (this.truncatedHMacOffered && allowTruncatedHMac())
+ {
+ TlsExtensionsUtils.addTruncatedHMacExtension(checkServerExtensions());
+ }
if (this.clientECPointFormats != null && TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
{
@@ -232,15 +242,13 @@ public abstract class AbstractTlsServer
* message including a Supported Point Formats Extension appends this extension (along
* with others) to its ServerHello message, enumerating the point formats it can parse.
*/
- this.serverECPointFormats = new short[]{ECPointFormat.ansiX962_compressed_char2,
- ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed};
+ this.serverECPointFormats = new short[]{ ECPointFormat.ansiX962_compressed_char2,
+ ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed };
- this.serverExtensions = new Hashtable();
- TlsECCUtils.addSupportedPointFormatsExtension(serverExtensions, serverECPointFormats);
- return serverExtensions;
+ TlsECCUtils.addSupportedPointFormatsExtension(checkServerExtensions(), serverECPointFormats);
}
- return null;
+ return serverExtensions;
}
public Vector getServerSupplementalData()
@@ -249,7 +257,14 @@ public abstract class AbstractTlsServer
return null;
}
+ public CertificateStatus getCertificateStatus()
+ throws IOException
+ {
+ return null;
+ }
+
public CertificateRequest getCertificateRequest()
+ throws IOException
{
return null;
}
@@ -296,9 +311,4 @@ public abstract class AbstractTlsServer
*/
return new NewSessionTicket(0L, TlsUtils.EMPTY_BYTES);
}
-
- public void notifyHandshakeComplete()
- throws IOException
- {
- }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java
index a0c24c7..3a1d631 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java
@@ -1,13 +1,38 @@
package org.bouncycastle.crypto.tls;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
public abstract class AbstractTlsSigner
implements TlsSigner
{
-
protected TlsContext context;
public void init(TlsContext context)
{
this.context = context;
}
+
+ public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+ throws CryptoException
+ {
+ return generateRawSignature(null, privateKey, md5AndSha1);
+ }
+
+ public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+ throws CryptoException
+ {
+ return verifyRawSignature(null, sigBytes, publicKey, md5AndSha1);
+ }
+
+ public Signer createSigner(AsymmetricKeyParameter privateKey)
+ {
+ return createSigner(null, privateKey);
+ }
+
+ public Signer createVerifyer(AsymmetricKeyParameter publicKey)
+ {
+ return createVerifyer(null, publicKey);
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java
new file mode 100644
index 0000000..3452f52
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsSignerCredentials
+ extends AbstractTlsCredentials
+ implements TlsSignerCredentials
+{
+ public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm()
+ {
+ throw new IllegalStateException("TlsSignerCredentials implementation does not support (D)TLS 1.2+");
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
index 5e3269b..91366be 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
@@ -5,11 +5,12 @@ package org.bouncycastle.crypto.tls;
*/
public class AlertDescription
{
-
/**
* This message notifies the recipient that the sender will not send any more messages on this
- * connection. The session becomes unresumable if any connection is terminated without proper
- * close_notify messages with level equal to warning.
+ * connection. Note that as of TLS 1.1, failure to properly close a connection no longer
+ * requires that a session not be resumed. This is a change from TLS 1.0 ("The session becomes
+ * unresumable if any connection is terminated without proper close_notify messages with level
+ * equal to warning.") to conform with widespread implementation practice.
*/
public static final short close_notify = 0;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
index 8b9d4ab..7642c4a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
@@ -25,12 +25,12 @@ public class ByteQueue
/**
* The initial size for our buffer.
*/
- private static final int INITBUFSIZE = 1024;
+ private static final int DEFAULT_CAPACITY = 1024;
/**
* The buffer where we store our data.
*/
- private byte[] databuf = new byte[ByteQueue.INITBUFSIZE];
+ private byte[] databuf;;
/**
* How many bytes at the beginning of the buffer are skipped.
@@ -42,6 +42,16 @@ public class ByteQueue
*/
private int available = 0;
+ public ByteQueue()
+ {
+ this(DEFAULT_CAPACITY);
+ }
+
+ public ByteQueue(int capacity)
+ {
+ databuf = new byte[capacity];
+ }
+
/**
* Read data from the buffer.
*
@@ -62,26 +72,34 @@ public class ByteQueue
+ " is too small for a read of " + len + " bytes");
}
System.arraycopy(databuf, skipped + skip, buf, offset, len);
- return;
}
/**
* Add some data to our buffer.
*
- * @param data A byte-array to read data from.
- * @param offset How many bytes to skip at the beginning of the array.
- * @param len How many bytes to read from the array.
+ * @param buf A byte-array to read data from.
+ * @param off How many bytes to skip at the beginning of the array.
+ * @param len How many bytes to read from the array.
*/
- public void addData(byte[] data, int offset, int len)
+ public void addData(byte[] buf, int off, int len)
{
if ((skipped + available + len) > databuf.length)
{
- byte[] tmp = new byte[ByteQueue.nextTwoPow(data.length)];
- System.arraycopy(databuf, skipped, tmp, 0, available);
+ int desiredSize = ByteQueue.nextTwoPow(available + len);
+ if (desiredSize > databuf.length)
+ {
+ byte[] tmp = new byte[desiredSize];
+ System.arraycopy(databuf, skipped, tmp, 0, available);
+ databuf = tmp;
+ }
+ else
+ {
+ System.arraycopy(databuf, skipped, databuf, 0, available);
+ }
skipped = 0;
- databuf = tmp;
}
- System.arraycopy(data, offset, databuf, skipped + available, len);
+
+ System.arraycopy(buf, off, databuf, skipped + available, len);
available += len;
}
@@ -102,15 +120,27 @@ public class ByteQueue
*/
available -= i;
skipped += i;
+ }
- /*
- * If more than half of our data is skipped, we will move the data in the buffer.
- */
- if (skipped > (databuf.length / 2))
- {
- System.arraycopy(databuf, skipped, databuf, 0, available);
- skipped = 0;
- }
+ /**
+ * Remove data from the buffer.
+ *
+ * @param buf The buffer where the removed data will be copied to.
+ * @param off How many bytes to skip at the beginning of buf.
+ * @param len How many bytes to read at all.
+ * @param skip How many bytes from our data to skip.
+ */
+ public void removeData(byte[] buf, int off, int len, int skip)
+ {
+ read(buf, off, len, skip);
+ removeData(skip + len);
+ }
+
+ public byte[] removeData(int len, int skip)
+ {
+ byte[] buf = new byte[len];
+ removeData(buf, 0, len, skip);
+ return buf;
}
/**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java
new file mode 100644
index 0000000..8902ed7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+/*
+ * RFC 3546 3.3.
+ */
+public class CertChainType
+{
+ public static final short individual_certs = 0;
+ public static final short pkipath = 1;
+
+ public static boolean isValid(short certChainType)
+ {
+ return certChainType >= individual_certs && certChainType <= pkipath;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
index fab79f4..02cf693 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
@@ -7,7 +7,6 @@ import java.io.OutputStream;
import java.util.Vector;
import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
/**
@@ -25,7 +24,6 @@ import org.bouncycastle.asn1.ASN1Primitive;
*/
public class Certificate
{
-
public static final Certificate EMPTY_CHAIN = new Certificate(
new org.bouncycastle.asn1.x509.Certificate[0]);
@@ -46,7 +44,7 @@ public class Certificate
*/
public org.bouncycastle.asn1.x509.Certificate[] getCerts()
{
- return clone(certificateList);
+ return getCertificateList();
}
/**
@@ -55,7 +53,7 @@ public class Certificate
*/
public org.bouncycastle.asn1.x509.Certificate[] getCertificateList()
{
- return clone(certificateList);
+ return cloneCertificateList();
}
public org.bouncycastle.asn1.x509.Certificate getCertificateAt(int index)
@@ -86,21 +84,23 @@ public class Certificate
public void encode(OutputStream output)
throws IOException
{
- Vector encCerts = new Vector(this.certificateList.length);
+ Vector derEncodings = new Vector(this.certificateList.length);
+
int totalLength = 0;
for (int i = 0; i < this.certificateList.length; ++i)
{
- byte[] encCert = certificateList[i].getEncoded(ASN1Encoding.DER);
- encCerts.addElement(encCert);
- totalLength += encCert.length + 3;
+ byte[] derEncoding = certificateList[i].getEncoded(ASN1Encoding.DER);
+ derEncodings.addElement(derEncoding);
+ totalLength += derEncoding.length + 3;
}
+ TlsUtils.checkUint24(totalLength);
TlsUtils.writeUint24(totalLength, output);
- for (int i = 0; i < encCerts.size(); ++i)
+ for (int i = 0; i < derEncodings.size(); ++i)
{
- byte[] encCert = (byte[])encCerts.elementAt(i);
- TlsUtils.writeOpaque24(encCert, output);
+ byte[] derEncoding = (byte[])derEncodings.elementAt(i);
+ TlsUtils.writeOpaque24(derEncoding, output);
}
}
@@ -114,40 +114,36 @@ public class Certificate
public static Certificate parse(InputStream input)
throws IOException
{
- org.bouncycastle.asn1.x509.Certificate[] certs;
- int left = TlsUtils.readUint24(input);
- if (left == 0)
+ int totalLength = TlsUtils.readUint24(input);
+ if (totalLength == 0)
{
return EMPTY_CHAIN;
}
- Vector tmp = new Vector();
- while (left > 0)
- {
- int size = TlsUtils.readUint24(input);
- left -= 3 + size;
- byte[] buf = TlsUtils.readFully(size, input);
+ byte[] certListData = TlsUtils.readFully(totalLength, input);
- ByteArrayInputStream bis = new ByteArrayInputStream(buf);
- ASN1Primitive asn1 = new ASN1InputStream(bis).readObject();
- TlsProtocol.assertEmpty(bis);
+ ByteArrayInputStream buf = new ByteArrayInputStream(certListData);
- tmp.addElement(org.bouncycastle.asn1.x509.Certificate.getInstance(asn1));
+ Vector certificate_list = new Vector();
+ while (buf.available() > 0)
+ {
+ byte[] derEncoding = TlsUtils.readOpaque24(buf);
+ ASN1Primitive asn1Cert = TlsUtils.readDERObject(derEncoding);
+ certificate_list.addElement(org.bouncycastle.asn1.x509.Certificate.getInstance(asn1Cert));
}
- certs = new org.bouncycastle.asn1.x509.Certificate[tmp.size()];
- for (int i = 0; i < tmp.size(); i++)
+
+ org.bouncycastle.asn1.x509.Certificate[] certificateList = new org.bouncycastle.asn1.x509.Certificate[certificate_list.size()];
+ for (int i = 0; i < certificate_list.size(); i++)
{
- certs[i] = (org.bouncycastle.asn1.x509.Certificate)tmp.elementAt(i);
+ certificateList[i] = (org.bouncycastle.asn1.x509.Certificate)certificate_list.elementAt(i);
}
- return new Certificate(certs);
+ return new Certificate(certificateList);
}
- private org.bouncycastle.asn1.x509.Certificate[] clone(org.bouncycastle.asn1.x509.Certificate[] list)
+ protected org.bouncycastle.asn1.x509.Certificate[] cloneCertificateList()
{
- org.bouncycastle.asn1.x509.Certificate[] rv = new org.bouncycastle.asn1.x509.Certificate[list.length];
-
- System.arraycopy(list, 0, rv, 0, rv.length);
-
- return rv;
+ org.bouncycastle.asn1.x509.Certificate[] result = new org.bouncycastle.asn1.x509.Certificate[certificateList.length];
+ System.arraycopy(certificateList, 0, result, 0, result.length);
+ return result;
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
index 00bf950..e9606e3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
@@ -25,8 +25,9 @@ import org.bouncycastle.asn1.x500.X500Name;
*/
public class CertificateRequest
{
- private short[] certificateTypes;
- private Vector certificateAuthorities;
+ protected short[] certificateTypes;
+ protected Vector supportedSignatureAlgorithms;
+ protected Vector certificateAuthorities;
/*
* TODO RFC 5264 7.4.4 A list of the hash/signature algorithm pairs that the server is able to
@@ -37,9 +38,10 @@ public class CertificateRequest
* @param certificateTypes see {@link ClientCertificateType} for valid constants.
* @param certificateAuthorities a {@link Vector} of {@link X500Name}.
*/
- public CertificateRequest(short[] certificateTypes, Vector certificateAuthorities)
+ public CertificateRequest(short[] certificateTypes, Vector supportedSignatureAlgorithms, Vector certificateAuthorities)
{
this.certificateTypes = certificateTypes;
+ this.supportedSignatureAlgorithms = supportedSignatureAlgorithms;
this.certificateAuthorities = certificateAuthorities;
}
@@ -53,6 +55,14 @@ public class CertificateRequest
}
/**
+ * @return a {@link Vector} of {@link SignatureAndHashAlgorithm} (or null before TLS 1.2).
+ */
+ public Vector getSupportedSignatureAlgorithms()
+ {
+ return supportedSignatureAlgorithms;
+ }
+
+ /**
* @return a {@link Vector} of {@link X500Name}
*/
public Vector getCertificateAuthorities()
@@ -69,15 +79,19 @@ public class CertificateRequest
public void encode(OutputStream output)
throws IOException
{
-
if (certificateTypes == null || certificateTypes.length == 0)
{
- TlsUtils.writeUint8((short)0, output);
+ TlsUtils.writeUint8(0, output);
}
else
{
- TlsUtils.writeUint8((short)certificateTypes.length, output);
- TlsUtils.writeUint8Array(certificateTypes, output);
+ TlsUtils.writeUint8ArrayWithUint8Length(certificateTypes, output);
+ }
+
+ if (supportedSignatureAlgorithms != null)
+ {
+ // TODO Check whether SignatureAlgorithm.anonymous is allowed here
+ TlsUtils.encodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, output);
}
if (certificateAuthorities == null || certificateAuthorities.isEmpty())
@@ -86,22 +100,23 @@ public class CertificateRequest
}
else
{
+ Vector derEncodings = new Vector(certificateAuthorities.size());
- Vector encDNs = new Vector(certificateAuthorities.size());
int totalLength = 0;
for (int i = 0; i < certificateAuthorities.size(); ++i)
{
- X500Name authorityDN = (X500Name)certificateAuthorities.elementAt(i);
- byte[] encDN = authorityDN.getEncoded(ASN1Encoding.DER);
- encDNs.addElement(encDN);
- totalLength += encDN.length;
+ X500Name certificateAuthority = (X500Name)certificateAuthorities.elementAt(i);
+ byte[] derEncoding = certificateAuthority.getEncoded(ASN1Encoding.DER);
+ derEncodings.addElement(derEncoding);
+ totalLength += derEncoding.length;
}
+ TlsUtils.checkUint16(totalLength);
TlsUtils.writeUint16(totalLength, output);
- for (int i = 0; i < encDNs.size(); ++i)
+ for (int i = 0; i < derEncodings.size(); ++i)
{
- byte[] encDN = (byte[])encDNs.elementAt(i);
+ byte[] encDN = (byte[])derEncodings.elementAt(i);
output.write(encDN);
}
}
@@ -109,12 +124,15 @@ public class CertificateRequest
/**
* Parse a {@link CertificateRequest} from an {@link InputStream}.
- *
- * @param input the {@link InputStream} to parse from.
+ *
+ * @param context
+ * the {@link TlsContext} of the current connection.
+ * @param input
+ * the {@link InputStream} to parse from.
* @return a {@link CertificateRequest} object.
* @throws IOException
*/
- public static CertificateRequest parse(InputStream input)
+ public static CertificateRequest parse(TlsContext context, InputStream input)
throws IOException
{
int numTypes = TlsUtils.readUint8(input);
@@ -124,17 +142,23 @@ public class CertificateRequest
certificateTypes[i] = TlsUtils.readUint8(input);
}
- byte[] authorities = TlsUtils.readOpaque16(input);
-
- Vector authorityDNs = new Vector();
+ Vector supportedSignatureAlgorithms = null;
+ if (TlsUtils.isTLSv12(context))
+ {
+ // TODO Check whether SignatureAlgorithm.anonymous is allowed here
+ supportedSignatureAlgorithms = TlsUtils.parseSupportedSignatureAlgorithms(false, input);
+ }
- ByteArrayInputStream bis = new ByteArrayInputStream(authorities);
+ Vector certificateAuthorities = new Vector();
+ byte[] certAuthData = TlsUtils.readOpaque16(input);
+ ByteArrayInputStream bis = new ByteArrayInputStream(certAuthData);
while (bis.available() > 0)
{
- byte[] dnBytes = TlsUtils.readOpaque16(bis);
- authorityDNs.addElement(X500Name.getInstance(ASN1Primitive.fromByteArray(dnBytes)));
+ byte[] derEncoding = TlsUtils.readOpaque16(bis);
+ ASN1Primitive asn1 = TlsUtils.readDERObject(derEncoding);
+ certificateAuthorities.addElement(X500Name.getInstance(asn1));
}
- return new CertificateRequest(certificateTypes, authorityDNs);
+ return new CertificateRequest(certificateTypes, supportedSignatureAlgorithms, certificateAuthorities);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java
new file mode 100644
index 0000000..34a0284
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ocsp.OCSPResponse;
+
+public class CertificateStatus
+{
+ protected short statusType;
+ protected Object response;
+
+ public CertificateStatus(short statusType, Object response)
+ {
+ if (!isCorrectType(statusType, response))
+ {
+ throw new IllegalArgumentException("'response' is not an instance of the correct type");
+ }
+
+ this.statusType = statusType;
+ this.response = response;
+ }
+
+ public short getStatusType()
+ {
+ return statusType;
+ }
+
+ public Object getResponse()
+ {
+ return response;
+ }
+
+ public OCSPResponse getOCSPResponse()
+ {
+ if (!isCorrectType(CertificateStatusType.ocsp, response))
+ {
+ throw new IllegalStateException("'response' is not an OCSPResponse");
+ }
+ return (OCSPResponse)response;
+ }
+
+ /**
+ * Encode this {@link CertificateStatus} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ TlsUtils.writeUint8(statusType, output);
+
+ switch (statusType)
+ {
+ case CertificateStatusType.ocsp:
+ byte[] derEncoding = ((OCSPResponse) response).getEncoded(ASN1Encoding.DER);
+ TlsUtils.writeOpaque24(derEncoding, output);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /**
+ * Parse a {@link CertificateStatus} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link CertificateStatus} object.
+ * @throws IOException
+ */
+ public static CertificateStatus parse(InputStream input) throws IOException
+ {
+ short status_type = TlsUtils.readUint8(input);
+ Object response;
+
+ switch (status_type)
+ {
+ case CertificateStatusType.ocsp:
+ {
+ byte[] derEncoding = TlsUtils.readOpaque24(input);
+ response = OCSPResponse.getInstance(TlsUtils.readDERObject(derEncoding));
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return new CertificateStatus(status_type, response);
+ }
+
+ protected static boolean isCorrectType(short statusType, Object response)
+ {
+ switch (statusType)
+ {
+ case CertificateStatusType.ocsp:
+ return response instanceof OCSPResponse;
+ default:
+ throw new IllegalArgumentException("'statusType' is an unsupported value");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java
new file mode 100644
index 0000000..b947c48
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class CertificateStatusRequest
+{
+ protected short statusType;
+ protected Object request;
+
+ public CertificateStatusRequest(short statusType, Object request)
+ {
+ if (!isCorrectType(statusType, request))
+ {
+ throw new IllegalArgumentException("'request' is not an instance of the correct type");
+ }
+
+ this.statusType = statusType;
+ this.request = request;
+ }
+
+ public short getStatusType()
+ {
+ return statusType;
+ }
+
+ public Object getRequest()
+ {
+ return request;
+ }
+
+ public OCSPStatusRequest getOCSPStatusRequest()
+ {
+ if (!isCorrectType(CertificateStatusType.ocsp, request))
+ {
+ throw new IllegalStateException("'request' is not an OCSPStatusRequest");
+ }
+ return (OCSPStatusRequest)request;
+ }
+
+ /**
+ * Encode this {@link CertificateStatusRequest} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ TlsUtils.writeUint8(statusType, output);
+
+ switch (statusType)
+ {
+ case CertificateStatusType.ocsp:
+ ((OCSPStatusRequest) request).encode(output);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /**
+ * Parse a {@link CertificateStatusRequest} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link CertificateStatusRequest} object.
+ * @throws IOException
+ */
+ public static CertificateStatusRequest parse(InputStream input) throws IOException
+ {
+ short status_type = TlsUtils.readUint8(input);
+ Object result;
+
+ switch (status_type)
+ {
+ case CertificateStatusType.ocsp:
+ result = OCSPStatusRequest.parse(input);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return new CertificateStatusRequest(status_type, result);
+ }
+
+ protected static boolean isCorrectType(short statusType, Object request)
+ {
+ switch (statusType)
+ {
+ case CertificateStatusType.ocsp:
+ return request instanceof OCSPStatusRequest;
+ default:
+ throw new IllegalArgumentException("'statusType' is an unsupported value");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java
new file mode 100644
index 0000000..bfe8298
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.crypto.tls;
+
+public class CertificateStatusType
+{
+ /*
+ * RFC 3546 3.6
+ */
+ public static final short ocsp = 1;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java
new file mode 100644
index 0000000..aab8908
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java
@@ -0,0 +1,133 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+/*
+ * RFC 3546 3.3
+ */
+public class CertificateURL
+{
+ protected short type;
+ protected Vector urlAndHashList;
+
+ /**
+ * @param type
+ * see {@link CertChainType} for valid constants.
+ * @param urlAndHashList
+ * a {@link Vector} of {@link URLAndHash}.
+ */
+ public CertificateURL(short type, Vector urlAndHashList)
+ {
+ if (!CertChainType.isValid(type))
+ {
+ throw new IllegalArgumentException("'type' is not a valid CertChainType value");
+ }
+ if (urlAndHashList == null || urlAndHashList.isEmpty())
+ {
+ throw new IllegalArgumentException("'urlAndHashList' must have length > 0");
+ }
+
+ this.type = type;
+ this.urlAndHashList = urlAndHashList;
+ }
+
+ /**
+ * @return {@link CertChainType}
+ */
+ public short getType()
+ {
+ return type;
+ }
+
+ /**
+ * @return a {@link Vector} of {@link URLAndHash}
+ */
+ public Vector getURLAndHashList()
+ {
+ return urlAndHashList;
+ }
+
+ /**
+ * Encode this {@link CertificateURL} to an {@link OutputStream}.
+ *
+ * @param output the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output)
+ throws IOException
+ {
+ TlsUtils.writeUint8(this.type, output);
+
+ ListBuffer16 buf = new ListBuffer16();
+ for (int i = 0; i < this.urlAndHashList.size(); ++i)
+ {
+ URLAndHash urlAndHash = (URLAndHash)this.urlAndHashList.elementAt(i);
+ urlAndHash.encode(buf);
+ }
+ buf.encodeTo(output);
+ }
+
+ /**
+ * Parse a {@link CertificateURL} from an {@link InputStream}.
+ *
+ * @param context
+ * the {@link TlsContext} of the current connection.
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link CertificateURL} object.
+ * @throws IOException
+ */
+ public static CertificateURL parse(TlsContext context, InputStream input)
+ throws IOException
+ {
+ short type = TlsUtils.readUint8(input);
+ if (!CertChainType.isValid(type))
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ int totalLength = TlsUtils.readUint16(input);
+ if (totalLength < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ byte[] urlAndHashListData = TlsUtils.readFully(totalLength, input);
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(urlAndHashListData);
+
+ Vector url_and_hash_list = new Vector();
+ while (buf.available() > 0)
+ {
+ URLAndHash url_and_hash = URLAndHash.parse(context, buf);
+ url_and_hash_list.addElement(url_and_hash);
+ }
+
+ return new CertificateURL(type, url_and_hash_list);
+ }
+
+ // TODO Could be more generally useful
+ class ListBuffer16 extends ByteArrayOutputStream
+ {
+ ListBuffer16() throws IOException
+ {
+ // Reserve space for length
+ TlsUtils.writeUint16(0, this);
+ }
+
+ void encodeTo(OutputStream output) throws IOException
+ {
+ // Patch actual length back in
+ int length = count - 2;
+ TlsUtils.checkUint16(length);
+ TlsUtils.writeUint16(length, buf, 0);
+ output.write(buf, 0, count);
+ buf = null;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java
new file mode 100644
index 0000000..a858ded
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.tls;
+
+public class ChangeCipherSpec
+{
+ public static final short change_cipher_spec = 1;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
index 2979cde..c1e7533 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
@@ -5,7 +5,6 @@ package org.bouncycastle.crypto.tls;
*/
public class CipherSuite
{
-
public static final int TLS_NULL_WITH_NULL_NULL = 0x0000;
public static final int TLS_RSA_WITH_NULL_MD5 = 0x0001;
public static final int TLS_RSA_WITH_NULL_SHA = 0x0002;
@@ -201,7 +200,98 @@ public class CipherSuite
public static final int TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032;
/*
+ * RFC 5487
+ */
+ public static final int TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8;
+ public static final int TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9;
+ public static final int TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA;
+ public static final int TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB;
+ public static final int TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC;
+ public static final int TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD;
+ public static final int TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE;
+ public static final int TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF;
+ public static final int TLS_PSK_WITH_NULL_SHA256 = 0x00B0;
+ public static final int TLS_PSK_WITH_NULL_SHA384 = 0x00B1;
+ public static final int TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2;
+ public static final int TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3;
+ public static final int TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4;
+ public static final int TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5;
+ public static final int TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6;
+ public static final int TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7;
+ public static final int TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8;
+ public static final int TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9;
+
+ /*
+ * RFC 5489
+ */
+ public static final int TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033;
+ public static final int TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034;
+ public static final int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035;
+ public static final int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036;
+ public static final int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037;
+ public static final int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038;
+ public static final int TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039;
+ public static final int TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A;
+ public static final int TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B;
+
+ /*
* RFC 5746
*/
public static final int TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
+
+ /*
+ * RFC 6655
+ */
+ public static final int TLS_RSA_WITH_AES_128_CCM = 0xC09C;
+ public static final int TLS_RSA_WITH_AES_256_CCM = 0xC09D;
+ public static final int TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E;
+ public static final int TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F;
+ public static final int TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0;
+ public static final int TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1;
+ public static final int TLS_DHE_RSA_WITH_AES_128_CCM_8 = 0xC0A2;
+ public static final int TLS_DHE_RSA_WITH_AES_256_CCM_8 = 0xC0A3;
+ public static final int TLS_PSK_WITH_AES_128_CCM = 0xC0A4;
+ public static final int TLS_PSK_WITH_AES_256_CCM = 0xC0A5;
+ public static final int TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6;
+ public static final int TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7;
+ public static final int TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8;
+ public static final int TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9;
+ public static final int TLS_PSK_DHE_WITH_AES_128_CCM_8 = 0xC0AA;
+ public static final int TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB;
+
+ /*
+ * TBD[draft-josefsson-salsa20-tls-02]
+ */
+ static final int TLS_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF00;
+ static final int TLS_RSA_WITH_SALSA20_SHA1 = 0xFF01;
+ static final int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF02;
+ static final int TLS_DHE_RSA_WITH_SALSA20_SHA1 = 0xFF03;
+ static final int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF04;
+ static final int TLS_ECDHE_RSA_WITH_SALSA20_SHA1 = 0xFF05;
+ static final int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF06;
+ static final int TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1 = 0xFF07;
+ static final int TLS_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF08;
+ static final int TLS_PSK_WITH_SALSA20_SHA1 = 0xFF09;
+ static final int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0A;
+ static final int TLS_DHE_PSK_WITH_SALSA20_SHA1 = 0xFF0B;
+ static final int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0C;
+ static final int TLS_RSA_PSK_WITH_SALSA20_SHA1 = 0xFF0D;
+ static final int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0E;
+ static final int TLS_ECDHE_PSK_WITH_SALSA20_SHA1 = 0xFF0F;
+ static final int TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF10;
+ static final int TLS_RSA_WITH_SALSA20_UMAC96 = 0xFF11;
+ static final int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF12;
+ static final int TLS_DHE_RSA_WITH_SALSA20_UMAC96 = 0xFF13;
+ static final int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF14;
+ static final int TLS_ECDHE_RSA_WITH_SALSA20_UMAC96 = 0xFF15;
+ static final int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF16;
+ static final int TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96 = 0xFF17;
+ static final int TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF18;
+ static final int TLS_PSK_WITH_SALSA20_UMAC96 = 0xFF19;
+ static final int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1A;
+ static final int TLS_DHE_PSK_WITH_SALSA20_UMAC96 = 0xFF1B;
+ static final int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1C;
+ static final int TLS_RSA_PSK_WITH_SALSA20_UMAC96 = 0xFF1D;
+ static final int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1E;
+ static final int TLS_ECDHE_PSK_WITH_SALSA20_UMAC96 = 0xFF1F;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
index 1a48491..43b73bf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
@@ -8,7 +8,6 @@ import org.bouncycastle.crypto.Digest;
class CombinedHash
implements TlsHandshakeHash
{
-
protected TlsContext context;
protected Digest md5;
protected Digest sha1;
@@ -31,16 +30,35 @@ class CombinedHash
this.context = context;
}
- public TlsHandshakeHash commit()
+ public TlsHandshakeHash notifyPRFDetermined()
{
return this;
}
- public TlsHandshakeHash fork()
+ public void trackHashAlgorithm(short hashAlgorithm)
+ {
+ throw new IllegalStateException("CombinedHash only supports calculating the legacy PRF for handshake hash");
+ }
+
+ public void sealHashAlgorithms()
+ {
+ }
+
+ public TlsHandshakeHash stopTracking()
{
return new CombinedHash(this);
}
+ public Digest forkPRFHash()
+ {
+ return new CombinedHash(this);
+ }
+
+ public byte[] getFinalHash(short hashAlgorithm)
+ {
+ throw new IllegalStateException("CombinedHash doesn't support multiple hashes");
+ }
+
/**
* @see org.bouncycastle.crypto.Digest#getAlgorithmName()
*/
@@ -80,7 +98,7 @@ class CombinedHash
*/
public int doFinal(byte[] out, int outOff)
{
- if (context != null && context.getServerVersion().isSSL())
+ if (context != null && TlsUtils.isSSL(context))
{
ssl3Complete(md5, SSL3Mac.IPAD, SSL3Mac.OPAD, 48);
ssl3Complete(sha1, SSL3Mac.IPAD, SSL3Mac.OPAD, 40);
@@ -102,15 +120,15 @@ class CombinedHash
protected void ssl3Complete(Digest d, byte[] ipad, byte[] opad, int padLength)
{
- byte[] secret = context.getSecurityParameters().masterSecret;
+ byte[] master_secret = context.getSecurityParameters().masterSecret;
- d.update(secret, 0, secret.length);
+ d.update(master_secret, 0, master_secret.length);
d.update(ipad, 0, padLength);
byte[] tmp = new byte[d.getDigestSize()];
d.doFinal(tmp, 0);
- d.update(secret, 0, secret.length);
+ d.update(master_secret, 0, master_secret.length);
d.update(opad, 0, padLength);
d.update(tmp, 0, tmp.length);
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java
index d814eac..65ed9b6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java
@@ -9,4 +9,5 @@ public class ContentType
public static final short alert = 21;
public static final short handshake = 22;
public static final short application_data = 23;
+ public static final short heartbeat = 24;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
index 8ccacfb..b3b1abe 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
@@ -13,7 +13,6 @@ import org.bouncycastle.util.Arrays;
public class DTLSClientProtocol
extends DTLSProtocol
{
-
public DTLSClientProtocol(SecureRandom secureRandom)
{
super(secureRandom);
@@ -22,7 +21,6 @@ public class DTLSClientProtocol
public DTLSTransport connect(TlsClient client, DatagramTransport transport)
throws IOException
{
-
if (client == null)
{
throw new IllegalArgumentException("'client' cannot be null");
@@ -43,6 +41,17 @@ public class DTLSClientProtocol
DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.clientContext, client, ContentType.handshake);
+ TlsSession sessionToResume = state.client.getSessionToResume();
+ if (sessionToResume != null)
+ {
+ SessionParameters sessionParameters = sessionToResume.exportSessionParameters();
+ if (sessionParameters != null)
+ {
+ state.tlsSession = sessionToResume;
+ state.sessionParameters = sessionParameters;
+ }
+ }
+
try
{
return clientHandshake(state, recordLayer);
@@ -67,7 +76,6 @@ public class DTLSClientProtocol
protected DTLSTransport clientHandshake(ClientHandshakeState state, DTLSRecordLayer recordLayer)
throws IOException
{
-
SecurityParameters securityParameters = state.clientContext.getSecurityParameters();
DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.clientContext, recordLayer);
@@ -76,23 +84,23 @@ public class DTLSClientProtocol
DTLSReliableHandshake.Message serverMessage = handshake.receiveMessage();
+ while (serverMessage.getType() == HandshakeType.hello_verify_request)
{
- // NOTE: After receiving a record from the server, we discover the record layer version
- ProtocolVersion server_version = recordLayer.getDiscoveredPeerVersion();
+ ProtocolVersion recordLayerVersion = recordLayer.resetDiscoveredPeerVersion();
ProtocolVersion client_version = state.clientContext.getClientVersion();
- if (!server_version.isEqualOrEarlierVersionOf(client_version))
+ /*
+ * RFC 6347 4.2.1 DTLS 1.2 server implementations SHOULD use DTLS version 1.0 regardless of
+ * the version of TLS that is expected to be negotiated. DTLS 1.2 and 1.0 clients MUST use
+ * the version solely to indicate packet formatting (which is the same in both DTLS 1.2 and
+ * 1.0) and not as part of version negotiation.
+ */
+ if (!recordLayerVersion.isEqualOrEarlierVersionOf(client_version))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- state.clientContext.setServerVersion(server_version);
- state.client.notifyServerVersion(server_version);
- }
-
- while (serverMessage.getType() == HandshakeType.hello_verify_request)
- {
- byte[] cookie = parseHelloVerifyRequest(state.clientContext, serverMessage.getBody());
+ byte[] cookie = processHelloVerifyRequest(state, serverMessage.getBody());
byte[] patched = patchClientHelloWithCookie(clientHelloBody, cookie);
handshake.resetHandshakeMessagesDigest();
@@ -103,16 +111,24 @@ public class DTLSClientProtocol
if (serverMessage.getType() == HandshakeType.server_hello)
{
+ reportServerVersion(state, recordLayer.getDiscoveredPeerVersion());
+
processServerHello(state, serverMessage.getBody());
- serverMessage = handshake.receiveMessage();
}
else
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.selectedCipherSuite);
+ if (state.maxFragmentLength >= 0)
+ {
+ int plainTextLimit = 1 << (8 + state.maxFragmentLength);
+ recordLayer.setPlaintextLimit(plainTextLimit);
+ }
+
+ securityParameters.cipherSuite = state.selectedCipherSuite;
securityParameters.compressionAlgorithm = state.selectedCompressionMethod;
+ securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.clientContext, state.selectedCipherSuite);
/*
* RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has
@@ -122,6 +138,48 @@ public class DTLSClientProtocol
handshake.notifyHelloComplete();
+ boolean resumedSession = state.selectedSessionID.length > 0 && state.tlsSession != null
+ && Arrays.areEqual(state.selectedSessionID, state.tlsSession.getSessionID());
+
+ if (resumedSession)
+ {
+ if (securityParameters.getCipherSuite() != state.sessionParameters.getCipherSuite()
+ || securityParameters.getCompressionAlgorithm() != state.sessionParameters.getCompressionAlgorithm())
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ securityParameters.masterSecret = Arrays.clone(state.sessionParameters.getMasterSecret());
+ recordLayer.initPendingEpoch(state.client.getCipher());
+
+ // NOTE: Calculated exclusive of the actual Finished message from the server
+ byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished,
+ TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null));
+ processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData);
+
+ // NOTE: Calculated exclusive of the Finished message itself
+ byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished,
+ TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null));
+ handshake.sendMessage(HandshakeType.finished, clientVerifyData);
+
+ handshake.finish();
+
+ state.clientContext.setResumableSession(state.tlsSession);
+
+ state.client.notifyHandshakeComplete();
+
+ return new DTLSTransport(recordLayer);
+ }
+
+ invalidateSession(state);
+
+ if (state.selectedSessionID.length > 0)
+ {
+ state.tlsSession = new TlsSessionImpl(state.selectedSessionID, null);
+ }
+
+ serverMessage = handshake.receiveMessage();
+
if (serverMessage.getType() == HandshakeType.supplemental_data)
{
processServerSupplementalData(state, serverMessage.getBody());
@@ -135,9 +193,11 @@ public class DTLSClientProtocol
state.keyExchange = state.client.getKeyExchange();
state.keyExchange.init(state.clientContext);
+ Certificate serverCertificate = null;
+
if (serverMessage.getType() == HandshakeType.certificate)
{
- processServerCertificate(state, serverMessage.getBody());
+ serverCertificate = processServerCertificate(state, serverMessage.getBody());
serverMessage = handshake.receiveMessage();
}
else
@@ -146,6 +206,22 @@ public class DTLSClientProtocol
state.keyExchange.skipServerCredentials();
}
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (serverCertificate == null || serverCertificate.isEmpty())
+ {
+ state.allowCertificateStatus = false;
+ }
+
+ if (serverMessage.getType() == HandshakeType.certificate_status)
+ {
+ processCertificateStatus(state, serverMessage.getBody());
+ serverMessage = handshake.receiveMessage();
+ }
+ else
+ {
+ // Okay, CertificateStatus is optional
+ }
+
if (serverMessage.getType() == HandshakeType.server_key_exchange)
{
processServerKeyExchange(state, serverMessage.getBody());
@@ -160,6 +236,14 @@ public class DTLSClientProtocol
if (serverMessage.getType() == HandshakeType.certificate_request)
{
processCertificateRequest(state, serverMessage.getBody());
+
+ /*
+ * TODO Give the client a chance to immediately select the CertificateVerify hash
+ * algorithm here to avoid tracking the other hash algorithms unnecessarily?
+ */
+ TlsUtils.trackHashAlgorithms(handshake.getHandshakeHash(),
+ state.certificateRequest.getSupportedSignatureAlgorithms());
+
serverMessage = handshake.receiveMessage();
}
else
@@ -179,6 +263,8 @@ public class DTLSClientProtocol
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
+ handshake.getHandshakeHash().sealHashAlgorithms();
+
Vector clientSupplementalData = state.client.getClientSupplementalData();
if (clientSupplementalData != null)
{
@@ -223,25 +309,45 @@ public class DTLSClientProtocol
handshake.sendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody);
TlsProtocol.establishMasterSecret(state.clientContext, state.keyExchange);
+ recordLayer.initPendingEpoch(state.client.getCipher());
+
+ TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish();
- if (state.clientCredentials instanceof TlsSignerCredentials)
+ if (state.clientCredentials != null && state.clientCredentials instanceof TlsSignerCredentials)
{
+ TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials;
+
/*
- * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended
- * from TLS 1.2
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
*/
- TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials;
- byte[] md5andsha1 = handshake.getCurrentHash();
- byte[] signature = signerCredentials.generateCertificateSignature(md5andsha1);
- byte[] certificateVerifyBody = generateCertificateVerify(state, signature);
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+ byte[] hash;
+
+ if (TlsUtils.isTLSv12(state.clientContext))
+ {
+ signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm();
+ if (signatureAndHashAlgorithm == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash());
+ }
+ else
+ {
+ signatureAndHashAlgorithm = null;
+ hash = TlsProtocol.getCurrentPRFHash(state.clientContext, prepareFinishHash, null);
+ }
+
+ byte[] signature = signerCredentials.generateCertificateSignature(hash);
+ DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ byte[] certificateVerifyBody = generateCertificateVerify(state, certificateVerify);
handshake.sendMessage(HandshakeType.certificate_verify, certificateVerifyBody);
}
- recordLayer.initPendingEpoch(state.client.getCipher());
-
// NOTE: Calculated exclusive of the Finished message itself
- byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, "client finished",
- handshake.getCurrentHash());
+ byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished,
+ TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null));
handshake.sendMessage(HandshakeType.finished, clientVerifyData);
if (state.expectSessionTicket)
@@ -258,39 +364,42 @@ public class DTLSClientProtocol
}
// NOTE: Calculated exclusive of the actual Finished message from the server
- byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, "server finished",
- handshake.getCurrentHash());
- serverMessage = handshake.receiveMessage();
+ byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished,
+ TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null));
+ processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData);
- if (serverMessage.getType() == HandshakeType.finished)
- {
- processFinished(serverMessage.getBody(), expectedServerVerifyData);
- }
- else
+ handshake.finish();
+
+ if (state.tlsSession != null)
{
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
+ state.sessionParameters = new SessionParameters.Builder()
+ .setCipherSuite(securityParameters.cipherSuite)
+ .setCompressionAlgorithm(securityParameters.compressionAlgorithm)
+ .setMasterSecret(securityParameters.masterSecret)
+ .setPeerCertificate(serverCertificate)
+ .build();
- handshake.finish();
+ state.tlsSession = TlsUtils.importSession(state.tlsSession.getSessionID(), state.sessionParameters);
+
+ state.clientContext.setResumableSession(state.tlsSession);
+ }
state.client.notifyHandshakeComplete();
return new DTLSTransport(recordLayer);
}
- protected byte[] generateCertificateVerify(ClientHandshakeState state, byte[] signature)
+ protected byte[] generateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify)
throws IOException
{
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeOpaque16(signature, buf);
+ certificateVerify.encode(buf);
return buf.toByteArray();
}
protected byte[] generateClientHello(ClientHandshakeState state, TlsClient client)
throws IOException
{
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ProtocolVersion client_version = client.getClientVersion();
@@ -304,8 +413,17 @@ public class DTLSClientProtocol
buf.write(state.clientContext.getSecurityParameters().getClientRandom());
- // Session id
- TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+ // Session ID
+ byte[] session_id = TlsUtils.EMPTY_BYTES;
+ if (state.tlsSession != null)
+ {
+ session_id = state.tlsSession.getSessionID();
+ if (session_id == null || session_id.length > 32)
+ {
+ session_id = TlsUtils.EMPTY_BYTES;
+ }
+ }
+ TlsUtils.writeOpaque8(session_id, buf);
// Cookie
TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
@@ -325,32 +443,26 @@ public class DTLSClientProtocol
* or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
* ClientHello. Including both is NOT RECOMMENDED.
*/
- boolean noRenegExt = state.clientExtensions == null
- || state.clientExtensions.get(TlsProtocol.EXT_RenegotiationInfo) == null;
+ byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo);
+ boolean noRenegExt = (null == renegExtData);
- int count = state.offeredCipherSuites.length;
- if (noRenegExt)
- {
- // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
- ++count;
- }
+ boolean noSCSV = !Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
- TlsUtils.writeUint16(2 * count, buf);
- TlsUtils.writeUint16Array(state.offeredCipherSuites, buf);
-
- if (noRenegExt)
+ if (noRenegExt && noSCSV)
{
- TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, buf);
+ // TODO Consider whether to default to a client extension instead
+ state.offeredCipherSuites = Arrays.append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
}
+
+ TlsUtils.writeUint16ArrayWithUint16Length(state.offeredCipherSuites, buf);
}
// TODO Add support for compression
// Compression methods
// state.offeredCompressionMethods = client.getCompressionMethods();
- state.offeredCompressionMethods = new short[]{CompressionMethod._null};
+ state.offeredCompressionMethods = new short[]{ CompressionMethod._null };
- TlsUtils.writeUint8((short)state.offeredCompressionMethods.length, buf);
- TlsUtils.writeUint8Array(state.offeredCompressionMethods, buf);
+ TlsUtils.writeUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf);
// Extensions
if (state.clientExtensions != null)
@@ -364,16 +476,29 @@ public class DTLSClientProtocol
protected byte[] generateClientKeyExchange(ClientHandshakeState state)
throws IOException
{
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
state.keyExchange.generateClientKeyExchange(buf);
return buf.toByteArray();
}
+ protected void invalidateSession(ClientHandshakeState state)
+ {
+ if (state.sessionParameters != null)
+ {
+ state.sessionParameters.clear();
+ state.sessionParameters = null;
+ }
+
+ if (state.tlsSession != null)
+ {
+ state.tlsSession.invalidate();
+ state.tlsSession = null;
+ }
+ }
+
protected void processCertificateRequest(ClientHandshakeState state, byte[] body)
throws IOException
{
-
if (state.authentication == null)
{
/*
@@ -385,19 +510,69 @@ public class DTLSClientProtocol
ByteArrayInputStream buf = new ByteArrayInputStream(body);
- state.certificateRequest = CertificateRequest.parse(buf);
+ state.certificateRequest = CertificateRequest.parse(state.clientContext, buf);
TlsProtocol.assertEmpty(buf);
state.keyExchange.validateCertificateRequest(state.certificateRequest);
}
- protected void processNewSessionTicket(ClientHandshakeState state, byte[] body)
+ protected void processCertificateStatus(ClientHandshakeState state, byte[] body)
throws IOException
{
+ if (!state.allowCertificateStatus)
+ {
+ /*
+ * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the
+ * server MUST have included an extension of type "status_request" with empty
+ * "extension_data" in the extended server hello..
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
ByteArrayInputStream buf = new ByteArrayInputStream(body);
+ state.certificateStatus = CertificateStatus.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ // TODO[RFC 3546] Figure out how to provide this to the client/authentication.
+ }
+
+ protected byte[] processHelloVerifyRequest(ClientHandshakeState state, byte[] body)
+ throws IOException
+ {
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+ ProtocolVersion server_version = TlsUtils.readVersion(buf);
+ byte[] cookie = TlsUtils.readOpaque8(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ // TODO Seems this behaviour is not yet in line with OpenSSL for DTLS 1.2
+// reportServerVersion(state, server_version);
+ if (!server_version.isEqualOrEarlierVersionOf(state.clientContext.getClientVersion()))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ /*
+ * RFC 6347 This specification increases the cookie size limit to 255 bytes for greater
+ * future flexibility. The limit remains 32 for previous versions of DTLS.
+ */
+ if (!ProtocolVersion.DTLSv12.isEqualOrEarlierVersionOf(server_version) && cookie.length > 32)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return cookie;
+ }
+
+ protected void processNewSessionTicket(ClientHandshakeState state, byte[] body)
+ throws IOException
+ {
+ ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf);
TlsProtocol.assertEmpty(buf);
@@ -405,10 +580,9 @@ public class DTLSClientProtocol
state.client.notifyNewSessionTicket(newSessionTicket);
}
- protected void processServerCertificate(ClientHandshakeState state, byte[] body)
+ protected Certificate processServerCertificate(ClientHandshakeState state, byte[] body)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
Certificate serverCertificate = Certificate.parse(buf);
@@ -418,34 +592,31 @@ public class DTLSClientProtocol
state.keyExchange.processServerCertificate(serverCertificate);
state.authentication = state.client.getAuthentication();
state.authentication.notifyServerCertificate(serverCertificate);
+
+ return serverCertificate;
}
protected void processServerHello(ClientHandshakeState state, byte[] body)
throws IOException
{
-
SecurityParameters securityParameters = state.clientContext.getSecurityParameters();
ByteArrayInputStream buf = new ByteArrayInputStream(body);
- // TODO Read RFCs for guidance on the expected record layer version number
ProtocolVersion server_version = TlsUtils.readVersion(buf);
- if (!server_version.equals(state.clientContext.getServerVersion()))
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
+ reportServerVersion(state, server_version);
securityParameters.serverRandom = TlsUtils.readFully(32, buf);
- byte[] sessionID = TlsUtils.readOpaque8(buf);
- if (sessionID.length > 32)
+ state.selectedSessionID = TlsUtils.readOpaque8(buf);
+ if (state.selectedSessionID.length > 32)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- state.client.notifySessionID(sessionID);
+ state.client.notifySessionID(state.selectedSessionID);
state.selectedCipherSuite = TlsUtils.readUint16(buf);
- if (!TlsProtocol.arrayContains(state.offeredCipherSuites, state.selectedCipherSuite)
+ if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite)
|| state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
|| state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
{
@@ -457,7 +628,7 @@ public class DTLSClientProtocol
state.client.notifySelectedCipherSuite(state.selectedCipherSuite);
state.selectedCompressionMethod = TlsUtils.readUint8(buf);
- if (!TlsProtocol.arrayContains(state.offeredCompressionMethods, state.selectedCompressionMethod))
+ if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
@@ -502,7 +673,7 @@ public class DTLSClientProtocol
* MUST continue to comply with Section 7.4.1.4 for all other extensions.
*/
if (!extType.equals(TlsProtocol.EXT_RenegotiationInfo)
- && (state.clientExtensions == null || state.clientExtensions.get(extType) == null))
+ && null == TlsUtils.getExtensionData(state.clientExtensions, extType))
{
/*
* RFC 3546 2.3 Note that for all extension types (including those defined in
@@ -524,8 +695,8 @@ public class DTLSClientProtocol
* When a ServerHello is received, the client MUST check if it includes the
* "renegotiation_info" extension:
*/
- byte[] renegExtValue = (byte[])serverExtensions.get(TlsProtocol.EXT_RenegotiationInfo);
- if (renegExtValue != null)
+ byte[] renegExtData = (byte[])serverExtensions.get(TlsProtocol.EXT_RenegotiationInfo);
+ if (renegExtData != null)
{
/*
* If the extension is present, set the secure_renegotiation flag to TRUE. The
@@ -535,7 +706,7 @@ public class DTLSClientProtocol
*/
state.secure_renegotiation = true;
- if (!Arrays.constantTimeAreEqual(renegExtValue,
+ if (!Arrays.constantTimeAreEqual(renegExtData,
TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
{
throw new TlsFatalAlert(AlertDescription.handshake_failure);
@@ -543,7 +714,16 @@ public class DTLSClientProtocol
}
}
- state.expectSessionTicket = serverExtensions.containsKey(TlsProtocol.EXT_SessionTicket);
+ state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, serverExtensions,
+ AlertDescription.illegal_parameter);
+
+ securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(serverExtensions);
+
+ state.allowCertificateStatus = TlsUtils.hasExpectedEmptyExtensionData(serverExtensions,
+ TlsExtensionsUtils.EXT_status_request, AlertDescription.illegal_parameter);
+
+ state.expectSessionTicket = TlsUtils.hasExpectedEmptyExtensionData(serverExtensions,
+ TlsProtocol.EXT_SessionTicket, AlertDescription.illegal_parameter);
}
state.client.notifySecureRenegotiation(state.secure_renegotiation);
@@ -557,7 +737,6 @@ public class DTLSClientProtocol
protected void processServerKeyExchange(ClientHandshakeState state, byte[] body)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
state.keyExchange.processServerKeyExchange(buf);
@@ -568,37 +747,30 @@ public class DTLSClientProtocol
protected void processServerSupplementalData(ClientHandshakeState state, byte[] body)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
Vector serverSupplementalData = TlsProtocol.readSupplementalDataMessage(buf);
state.client.processServerSupplementalData(serverSupplementalData);
}
- protected static byte[] parseHelloVerifyRequest(TlsContext context, byte[] body)
+ protected void reportServerVersion(ClientHandshakeState state, ProtocolVersion server_version)
throws IOException
{
-
- ByteArrayInputStream buf = new ByteArrayInputStream(body);
-
- ProtocolVersion server_version = TlsUtils.readVersion(buf);
- if (!server_version.equals(context.getServerVersion()))
+ TlsClientContextImpl clientContext = state.clientContext;
+ ProtocolVersion currentServerVersion = clientContext.getServerVersion();
+ if (null == currentServerVersion)
+ {
+ clientContext.setServerVersion(server_version);
+ state.client.notifyServerVersion(server_version);
+ }
+ else if (!currentServerVersion.equals(server_version))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
-
- byte[] cookie = TlsUtils.readOpaque8(buf);
-
- // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347
-
- TlsProtocol.assertEmpty(buf);
-
- return cookie;
}
protected static byte[] patchClientHelloWithCookie(byte[] clientHelloBody, byte[] cookie)
throws IOException
{
-
int sessionIDPos = 34;
int sessionIDLength = TlsUtils.readUint8(clientHelloBody, sessionIDPos);
@@ -607,7 +779,8 @@ public class DTLSClientProtocol
byte[] patched = new byte[clientHelloBody.length + cookie.length];
System.arraycopy(clientHelloBody, 0, patched, 0, cookieLengthPos);
- TlsUtils.writeUint8((short)cookie.length, patched, cookieLengthPos);
+ TlsUtils.checkUint8(cookie.length);
+ TlsUtils.writeUint8(cookie.length, patched, cookieLengthPos);
System.arraycopy(cookie, 0, patched, cookiePos, cookie.length);
System.arraycopy(clientHelloBody, cookiePos, patched, cookiePos + cookie.length, clientHelloBody.length
- cookiePos);
@@ -619,15 +792,22 @@ public class DTLSClientProtocol
{
TlsClient client = null;
TlsClientContextImpl clientContext = null;
+ TlsSession tlsSession = null;
+ SessionParameters sessionParameters = null;
+ SessionParameters.Builder sessionParametersBuilder = null;
int[] offeredCipherSuites = null;
short[] offeredCompressionMethods = null;
Hashtable clientExtensions = null;
+ byte[] selectedSessionID = null;
int selectedCipherSuite = -1;
short selectedCompressionMethod = -1;
boolean secure_renegotiation = false;
+ short maxFragmentLength = -1;
+ boolean allowCertificateStatus = false;
boolean expectSessionTicket = false;
TlsKeyExchange keyExchange = null;
TlsAuthentication authentication = null;
+ CertificateStatus certificateStatus = null;
CertificateRequest certificateRequest = null;
TlsCredentials clientCredentials = null;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
index 2789b22..e27580c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
@@ -4,18 +4,17 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
+import java.util.Hashtable;
import java.util.Vector;
import org.bouncycastle.util.Arrays;
public abstract class DTLSProtocol
{
-
protected final SecureRandom secureRandom;
protected DTLSProtocol(SecureRandom secureRandom)
{
-
if (secureRandom == null)
{
throw new IllegalArgumentException("'secureRandom' cannot be null");
@@ -27,7 +26,6 @@ public abstract class DTLSProtocol
protected void processFinished(byte[] body, byte[] expected_verify_data)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf);
@@ -40,10 +38,20 @@ public abstract class DTLSProtocol
}
}
+ protected static short evaluateMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions,
+ short alertDescription) throws IOException
+ {
+ short maxFragmentLength = TlsExtensionsUtils.getMaxFragmentLengthExtension(serverExtensions);
+ if (maxFragmentLength >= 0 && maxFragmentLength != TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions))
+ {
+ throw new TlsFatalAlert(alertDescription);
+ }
+ return maxFragmentLength;
+ }
+
protected static byte[] generateCertificate(Certificate certificate)
throws IOException
{
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
certificate.encode(buf);
return buf.toByteArray();
@@ -52,7 +60,6 @@ public abstract class DTLSProtocol
protected static byte[] generateSupplementalData(Vector supplementalData)
throws IOException
{
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
TlsProtocol.writeSupplementalData(buf, supplementalData);
return buf.toByteArray();
@@ -61,7 +68,6 @@ public abstract class DTLSProtocol
protected static void validateSelectedCipherSuite(int selectedCipherSuite, short alertDescription)
throws IOException
{
-
switch (selectedCipherSuite)
{
case CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5:
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
index 3fde01a..cba13d5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
@@ -5,7 +5,6 @@ import java.io.IOException;
class DTLSRecordLayer
implements DatagramTransport
{
-
private static final int RECORD_HEADER_LENGTH = 13;
private static final int MAX_FRAGMENT_LENGTH = 1 << 14;
private static final long TCP_MSL = 1000L * 60 * 2;
@@ -21,6 +20,7 @@ class DTLSRecordLayer
private volatile boolean failed = false;
private volatile ProtocolVersion discoveredPeerVersion = null;
private volatile boolean inHandshake;
+ private volatile int plaintextLimit;
private DTLSEpoch currentEpoch, pendingEpoch;
private DTLSEpoch readEpoch, writeEpoch;
@@ -40,6 +40,13 @@ class DTLSRecordLayer
this.pendingEpoch = null;
this.readEpoch = currentEpoch;
this.writeEpoch = currentEpoch;
+
+ setPlaintextLimit(MAX_FRAGMENT_LENGTH);
+ }
+
+ void setPlaintextLimit(int plaintextLimit)
+ {
+ this.plaintextLimit = plaintextLimit;
}
ProtocolVersion getDiscoveredPeerVersion()
@@ -47,6 +54,13 @@ class DTLSRecordLayer
return discoveredPeerVersion;
}
+ ProtocolVersion resetDiscoveredPeerVersion()
+ {
+ ProtocolVersion result = discoveredPeerVersion;
+ discoveredPeerVersion = null;
+ return result;
+ }
+
void initPendingEpoch(TlsCipher pendingCipher)
{
if (pendingEpoch != null)
@@ -99,26 +113,24 @@ class DTLSRecordLayer
public int getReceiveLimit()
throws IOException
{
- return Math.min(MAX_FRAGMENT_LENGTH,
+ return Math.min(this.plaintextLimit,
readEpoch.getCipher().getPlaintextLimit(transport.getReceiveLimit() - RECORD_HEADER_LENGTH));
}
public int getSendLimit()
throws IOException
{
- return Math.min(MAX_FRAGMENT_LENGTH,
+ return Math.min(this.plaintextLimit,
writeEpoch.getCipher().getPlaintextLimit(transport.getSendLimit() - RECORD_HEADER_LENGTH));
}
public int receive(byte[] buf, int off, int len, int waitMillis)
throws IOException
{
-
byte[] record = null;
- for (; ; )
+ for (;;)
{
-
int receiveLimit = Math.min(len, getReceiveLimit()) + RECORD_HEADER_LENGTH;
if (record == null || record.length < receiveLimit)
{
@@ -157,6 +169,7 @@ class DTLSRecordLayer
case ContentType.application_data:
case ContentType.change_cipher_spec:
case ContentType.handshake:
+ case ContentType.heartbeat:
break;
default:
// TODO Exception?
@@ -199,6 +212,11 @@ class DTLSRecordLayer
recordEpoch.getReplayWindow().reportAuthenticated(seq);
+ if (plaintext.length > this.plaintextLimit)
+ {
+ continue;
+ }
+
if (discoveredPeerVersion == null)
{
discoveredPeerVersion = version;
@@ -208,7 +226,6 @@ class DTLSRecordLayer
{
case ContentType.alert:
{
-
if (plaintext.length == 2)
{
short alertLevel = plaintext[0];
@@ -249,14 +266,18 @@ class DTLSRecordLayer
{
// Implicitly receive change_cipher_spec and change to pending cipher state
- if (plaintext.length != 1 || plaintext[0] != 1)
+ for (int i = 0; i < plaintext.length; ++i)
{
- continue;
- }
+ short message = TlsUtils.readUint8(plaintext, i);
+ if (message != ChangeCipherSpec.change_cipher_spec)
+ {
+ continue;
+ }
- if (pendingEpoch != null)
- {
- readEpoch = pendingEpoch;
+ if (pendingEpoch != null)
+ {
+ readEpoch = pendingEpoch;
+ }
}
continue;
@@ -273,6 +294,12 @@ class DTLSRecordLayer
// TODO Consider support for HelloRequest
continue;
}
+ break;
+ }
+ case ContentType.heartbeat:
+ {
+ // TODO[RFC 6520]
+ continue;
}
}
@@ -300,18 +327,15 @@ class DTLSRecordLayer
public void send(byte[] buf, int off, int len)
throws IOException
{
-
short contentType = ContentType.application_data;
if (this.inHandshake || this.writeEpoch == this.retransmitEpoch)
{
-
contentType = ContentType.handshake;
short handshakeType = TlsUtils.readUint8(buf, off);
if (handshakeType == HandshakeType.finished)
{
-
DTLSEpoch nextEpoch = null;
if (this.inHandshake)
{
@@ -331,7 +355,7 @@ class DTLSRecordLayer
// Implicitly send change_cipher_spec and change to pending cipher state
// TODO Send change_cipher_spec and finished records in single datagram?
- byte[] data = new byte[]{1};
+ byte[] data = new byte[]{ 1 };
sendRecord(ContentType.change_cipher_spec, data, 0, data.length);
writeEpoch = nextEpoch;
@@ -410,7 +434,6 @@ class DTLSRecordLayer
private void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause)
throws IOException
{
-
peer.notifyAlertRaised(alertLevel, alertDescription, message, cause);
byte[] error = new byte[2];
@@ -434,8 +457,7 @@ class DTLSRecordLayer
}
int received = Math.min(recordQueue.size(), RECORD_HEADER_LENGTH + length);
- recordQueue.read(buf, off, received, 0);
- recordQueue.removeData(received);
+ recordQueue.removeData(buf, off, received, 0);
return received;
}
@@ -457,6 +479,10 @@ class DTLSRecordLayer
private void sendRecord(short contentType, byte[] buf, int off, int len)
throws IOException
{
+ if (len > this.plaintextLimit)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
/*
* RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
@@ -473,10 +499,7 @@ class DTLSRecordLayer
byte[] ciphertext = writeEpoch.getCipher().encodePlaintext(
getMacSequenceNumber(recordEpoch, recordSequenceNumber), contentType, buf, off, len);
- if (ciphertext.length > MAX_FRAGMENT_LENGTH)
- {
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
+ // TODO Check the ciphertext length?
byte[] record = new byte[ciphertext.length + RECORD_HEADER_LENGTH];
TlsUtils.writeUint8(contentType, record, 0);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
index 3819251..84ccfcb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
@@ -10,12 +10,11 @@ import org.bouncycastle.util.Integers;
class DTLSReliableHandshake
{
-
private final static int MAX_RECEIVE_AHEAD = 10;
private final DTLSRecordLayer recordLayer;
- private TlsHandshakeHash hash = new DeferredHash();
+ private TlsHandshakeHash handshakeHash;
private Hashtable currentInboundFlight = new Hashtable();
private Hashtable previousInboundFlight = null;
@@ -27,25 +26,31 @@ class DTLSReliableHandshake
DTLSReliableHandshake(TlsContext context, DTLSRecordLayer transport)
{
this.recordLayer = transport;
- this.hash.init(context);
+ this.handshakeHash = new DeferredHash();
+ this.handshakeHash.init(context);
}
void notifyHelloComplete()
{
- this.hash = this.hash.commit();
+ this.handshakeHash = handshakeHash.notifyPRFDetermined();
+ }
+
+ TlsHandshakeHash getHandshakeHash()
+ {
+ return handshakeHash;
}
- byte[] getCurrentHash()
+ TlsHandshakeHash prepareToFinish()
{
- TlsHandshakeHash copyOfHash = hash.fork();
- byte[] result = new byte[copyOfHash.getDigestSize()];
- copyOfHash.doFinal(result, 0);
+ TlsHandshakeHash result = handshakeHash;
+ this.handshakeHash = handshakeHash.stopTracking();
return result;
}
void sendMessage(short msg_type, byte[] body)
throws IOException
{
+ TlsUtils.checkUint24(body.length);
if (!sending)
{
@@ -62,10 +67,21 @@ class DTLSReliableHandshake
updateHandshakeMessagesDigest(message);
}
- Message receiveMessage()
+ byte[] receiveMessageBody(short msg_type)
throws IOException
{
+ Message message = receiveMessage();
+ if (message.getType() != msg_type)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ return message.getBody();
+ }
+
+ Message receiveMessage()
+ throws IOException
+ {
if (sending)
{
sending = false;
@@ -93,7 +109,6 @@ class DTLSReliableHandshake
for (; ; )
{
-
int receiveLimit = recordLayer.getReceiveLimit();
if (buf == null || buf.length < receiveLimit)
{
@@ -280,7 +295,7 @@ class DTLSReliableHandshake
void resetHandshakeMessagesDigest()
{
- hash.reset();
+ handshakeHash.reset();
}
/**
@@ -328,8 +343,8 @@ class DTLSReliableHandshake
TlsUtils.writeUint16(message.getSeq(), buf, 4);
TlsUtils.writeUint24(0, buf, 6);
TlsUtils.writeUint24(body.length, buf, 9);
- hash.update(buf, 0, buf.length);
- hash.update(body, 0, body.length);
+ handshakeHash.update(buf, 0, buf.length);
+ handshakeHash.update(body, 0, body.length);
}
return message;
}
@@ -337,7 +352,6 @@ class DTLSReliableHandshake
private void writeMessage(Message message)
throws IOException
{
-
int sendLimit = recordLayer.getSendLimit();
int fragmentLimit = sendLimit - 12;
@@ -364,18 +378,15 @@ class DTLSReliableHandshake
private void writeHandshakeFragment(Message message, int fragment_offset, int fragment_length)
throws IOException
{
-
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeUint8(message.getType(), buf);
- TlsUtils.writeUint24(message.getBody().length, buf);
- TlsUtils.writeUint16(message.getSeq(), buf);
- TlsUtils.writeUint24(fragment_offset, buf);
- TlsUtils.writeUint24(fragment_length, buf);
- buf.write(message.getBody(), fragment_offset, fragment_length);
-
- byte[] fragment = buf.toByteArray();
-
- recordLayer.send(fragment, 0, fragment.length);
+ RecordLayerBuffer fragment = new RecordLayerBuffer(12 + fragment_length);
+ TlsUtils.writeUint8(message.getType(), fragment);
+ TlsUtils.writeUint24(message.getBody().length, fragment);
+ TlsUtils.writeUint16(message.getSeq(), fragment);
+ TlsUtils.writeUint24(fragment_offset, fragment);
+ TlsUtils.writeUint24(fragment_length, fragment);
+ fragment.write(message.getBody(), fragment_offset, fragment_length);
+
+ fragment.sendToRecordLayer(recordLayer);
}
private static boolean checkAll(Hashtable inboundFlight)
@@ -402,7 +413,6 @@ class DTLSReliableHandshake
static class Message
{
-
private final int message_seq;
private final short msg_type;
private final byte[] body;
@@ -429,4 +439,18 @@ class DTLSReliableHandshake
return body;
}
}
+
+ static class RecordLayerBuffer extends ByteArrayOutputStream
+ {
+ RecordLayerBuffer(int size)
+ {
+ super(size);
+ }
+
+ void sendToRecordLayer(DTLSRecordLayer recordLayer) throws IOException
+ {
+ recordLayer.send(buf, 0, count);
+ buf = null;
+ }
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
index 3a100d1..fbb3336 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
@@ -15,7 +15,6 @@ import org.bouncycastle.util.Arrays;
public class DTLSServerProtocol
extends DTLSProtocol
{
-
protected boolean verifyRequests = true;
public DTLSServerProtocol(SecureRandom secureRandom)
@@ -36,7 +35,6 @@ public class DTLSServerProtocol
public DTLSTransport accept(TlsServer server, DatagramTransport transport)
throws IOException
{
-
if (server == null)
{
throw new IllegalArgumentException("'server' cannot be null");
@@ -80,10 +78,9 @@ public class DTLSServerProtocol
}
}
- public DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer)
+ protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer)
throws IOException
{
-
SecurityParameters securityParameters = state.serverContext.getSecurityParameters();
DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.serverContext, recordLayer);
@@ -105,23 +102,31 @@ public class DTLSServerProtocol
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- byte[] serverHelloBody = generateServerHello(state);
- handshake.sendMessage(HandshakeType.server_hello, serverHelloBody);
-
- // TODO This block could really be done before actually sending the hello
{
- securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.selectedCipherSuite);
+ byte[] serverHelloBody = generateServerHello(state);
+
+ if (state.maxFragmentLength >= 0)
+ {
+ int plainTextLimit = 1 << (8 + state.maxFragmentLength);
+ recordLayer.setPlaintextLimit(plainTextLimit);
+ }
+
+ securityParameters.cipherSuite = state.selectedCipherSuite;
securityParameters.compressionAlgorithm = state.selectedCompressionMethod;
-
+ securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.serverContext,
+ state.selectedCipherSuite);
+
/*
* RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length
* has a verify_data_length equal to 12. This includes all existing cipher suites.
*/
securityParameters.verifyDataLength = 12;
-
- handshake.notifyHelloComplete();
+
+ handshake.sendMessage(HandshakeType.server_hello, serverHelloBody);
}
+ handshake.notifyHelloComplete();
+
Vector serverSupplementalData = state.server.getServerSupplementalData();
if (serverSupplementalData != null)
{
@@ -133,6 +138,9 @@ public class DTLSServerProtocol
state.keyExchange.init(state.serverContext);
state.serverCredentials = state.server.getCredentials();
+
+ Certificate serverCertificate = null;
+
if (state.serverCredentials == null)
{
state.keyExchange.skipServerCredentials();
@@ -141,10 +149,27 @@ public class DTLSServerProtocol
{
state.keyExchange.processServerCredentials(state.serverCredentials);
- byte[] certificateBody = generateCertificate(state.serverCredentials.getCertificate());
+ serverCertificate = state.serverCredentials.getCertificate();
+ byte[] certificateBody = generateCertificate(serverCertificate);
handshake.sendMessage(HandshakeType.certificate, certificateBody);
}
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (serverCertificate == null || serverCertificate.isEmpty())
+ {
+ state.allowCertificateStatus = false;
+ }
+
+ if (state.allowCertificateStatus)
+ {
+ CertificateStatus certificateStatus = state.server.getCertificateStatus();
+ if (certificateStatus != null)
+ {
+ byte[] certificateStatusBody = generateCertificateStatus(state, certificateStatus);
+ handshake.sendMessage(HandshakeType.certificate_status, certificateStatusBody);
+ }
+ }
+
byte[] serverKeyExchange = state.keyExchange.generateServerKeyExchange();
if (serverKeyExchange != null)
{
@@ -160,11 +185,16 @@ public class DTLSServerProtocol
byte[] certificateRequestBody = generateCertificateRequest(state, state.certificateRequest);
handshake.sendMessage(HandshakeType.certificate_request, certificateRequestBody);
+
+ TlsUtils.trackHashAlgorithms(handshake.getHandshakeHash(),
+ state.certificateRequest.getSupportedSignatureAlgorithms());
}
}
handshake.sendMessage(HandshakeType.server_hello_done, TlsUtils.EMPTY_BYTES);
+ handshake.getHandshakeHash().sealHashAlgorithms();
+
clientMessage = handshake.receiveMessage();
if (clientMessage.getType() == HandshakeType.supplemental_data)
@@ -190,9 +220,7 @@ public class DTLSServerProtocol
}
else
{
- ProtocolVersion equivalentTLSVersion = state.serverContext.getServerVersion().getEquivalentTLSVersion();
-
- if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(equivalentTLSVersion))
+ if (TlsUtils.isTLSv12(state.serverContext))
{
/*
* RFC 5246 If no suitable certificate is available, the client MUST send a
@@ -216,8 +244,11 @@ public class DTLSServerProtocol
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
+ TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange);
recordLayer.initPendingEpoch(state.server.getCipher());
+ TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish();
+
/*
* RFC 5246 7.4.8 This message is only sent following a client certificate that has signing
* capability (i.e., all certificates except those containing fixed Diffie-Hellman
@@ -225,33 +256,14 @@ public class DTLSServerProtocol
*/
if (expectCertificateVerifyMessage(state))
{
- byte[] certificateVerifyHash = handshake.getCurrentHash();
- clientMessage = handshake.receiveMessage();
-
- if (clientMessage.getType() == HandshakeType.certificate_verify)
- {
- processCertificateVerify(state, clientMessage.getBody(), certificateVerifyHash);
- }
- else
- {
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
+ byte[] certificateVerifyBody = handshake.receiveMessageBody(HandshakeType.certificate_verify);
+ processCertificateVerify(state, certificateVerifyBody, prepareFinishHash);
}
// NOTE: Calculated exclusive of the actual Finished message from the client
- byte[] clientFinishedHash = handshake.getCurrentHash();
- clientMessage = handshake.receiveMessage();
-
- if (clientMessage.getType() == HandshakeType.finished)
- {
- byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, "client finished",
- clientFinishedHash);
- processFinished(clientMessage.getBody(), expectedClientVerifyData);
- }
- else
- {
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
+ byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.client_finished,
+ TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null));
+ processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedClientVerifyData);
if (state.expectSessionTicket)
{
@@ -261,8 +273,8 @@ public class DTLSServerProtocol
}
// NOTE: Calculated exclusive of the Finished message itself
- byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, "server finished",
- handshake.getCurrentHash());
+ byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.server_finished,
+ TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null));
handshake.sendMessage(HandshakeType.finished, serverVerifyData);
handshake.finish();
@@ -275,16 +287,22 @@ public class DTLSServerProtocol
protected byte[] generateCertificateRequest(ServerHandshakeState state, CertificateRequest certificateRequest)
throws IOException
{
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
certificateRequest.encode(buf);
return buf.toByteArray();
}
- protected byte[] generateNewSessionTicket(ServerHandshakeState state, NewSessionTicket newSessionTicket)
+ protected byte[] generateCertificateStatus(ServerHandshakeState state, CertificateStatus certificateStatus)
throws IOException
{
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ certificateStatus.encode(buf);
+ return buf.toByteArray();
+ }
+ protected byte[] generateNewSessionTicket(ServerHandshakeState state, NewSessionTicket newSessionTicket)
+ throws IOException
+ {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
newSessionTicket.encode(buf);
return buf.toByteArray();
@@ -293,6 +311,7 @@ public class DTLSServerProtocol
protected byte[] generateServerHello(ServerHandshakeState state)
throws IOException
{
+ SecurityParameters securityParameters = state.serverContext.getSecurityParameters();
ByteArrayOutputStream buf = new ByteArrayOutputStream();
@@ -310,7 +329,7 @@ public class DTLSServerProtocol
TlsUtils.writeVersion(state.serverContext.getServerVersion(), buf);
- buf.write(state.serverContext.getSecurityParameters().serverRandom);
+ buf.write(securityParameters.getServerRandom());
/*
* The server may return an empty session_id to indicate that the session will not be cached
@@ -319,7 +338,7 @@ public class DTLSServerProtocol
TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
state.selectedCipherSuite = state.server.getSelectedCipherSuite();
- if (!TlsProtocol.arrayContains(state.offeredCipherSuites, state.selectedCipherSuite)
+ if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite)
|| state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
|| state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
{
@@ -329,7 +348,7 @@ public class DTLSServerProtocol
validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.internal_error);
state.selectedCompressionMethod = state.server.getSelectedCompressionMethod();
- if (!TlsProtocol.arrayContains(state.offeredCompressionMethods, state.selectedCompressionMethod))
+ if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
@@ -344,9 +363,8 @@ public class DTLSServerProtocol
*/
if (state.secure_renegotiation)
{
-
- boolean noRenegExt = state.serverExtensions == null
- || !state.serverExtensions.containsKey(TlsProtocol.EXT_RenegotiationInfo);
+ byte[] renegExtData = TlsUtils.getExtensionData(state.serverExtensions, TlsProtocol.EXT_RenegotiationInfo);
+ boolean noRenegExt = (null == renegExtData);
if (noRenegExt)
{
@@ -357,15 +375,12 @@ public class DTLSServerProtocol
* because the client is signaling its willingness to receive the extension via the
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
*/
- if (state.serverExtensions == null)
- {
- state.serverExtensions = new Hashtable();
- }
/*
* If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
* "renegotiation_info" extension in the ServerHello message.
*/
+ state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.serverExtensions);
state.serverExtensions.put(TlsProtocol.EXT_RenegotiationInfo,
TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
}
@@ -373,7 +388,17 @@ public class DTLSServerProtocol
if (state.serverExtensions != null)
{
- state.expectSessionTicket = state.serverExtensions.containsKey(TlsProtocol.EXT_SessionTicket);
+ state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, state.serverExtensions,
+ AlertDescription.internal_error);
+
+ securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(state.serverExtensions);
+
+ state.allowCertificateStatus = TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions,
+ TlsExtensionsUtils.EXT_status_request, AlertDescription.internal_error);
+
+ state.expectSessionTicket = TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions,
+ TlsProtocol.EXT_SessionTicket, AlertDescription.internal_error);
+
TlsProtocol.writeExtensions(buf, state.serverExtensions);
}
@@ -383,7 +408,6 @@ public class DTLSServerProtocol
protected void notifyClientCertificate(ServerHandshakeState state, Certificate clientCertificate)
throws IOException
{
-
if (state.certificateRequest == null)
{
throw new IllegalStateException();
@@ -429,7 +453,6 @@ public class DTLSServerProtocol
protected void processClientCertificate(ServerHandshakeState state, byte[] body)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
Certificate clientCertificate = Certificate.parse(buf);
@@ -439,27 +462,29 @@ public class DTLSServerProtocol
notifyClientCertificate(state, clientCertificate);
}
- protected void processCertificateVerify(ServerHandshakeState state, byte[] body, byte[] certificateVerifyHash)
+ protected void processCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
- byte[] clientCertificateSignature = TlsUtils.readOpaque16(buf);
+ DigitallySigned clientCertificateVerify = DigitallySigned.parse(state.serverContext, buf);
TlsProtocol.assertEmpty(buf);
// Verify the CertificateVerify message contains a correct signature.
try
{
- TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType);
- tlsSigner.init(state.serverContext);
+ // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned
+ byte[] certificateVerifyHash = TlsProtocol.getCurrentPRFHash(state.serverContext, prepareFinishHash, null);
org.bouncycastle.asn1.x509.Certificate x509Cert = state.clientCertificate.getCertificateAt(0);
SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
- tlsSigner.verifyRawSignature(clientCertificateSignature, publicKey, certificateVerifyHash);
+ TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType);
+ tlsSigner.init(state.serverContext);
+ tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
+ clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash);
}
catch (Exception e)
{
@@ -470,7 +495,6 @@ public class DTLSServerProtocol
protected void processClientHello(ServerHandshakeState state, byte[] body)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
// TODO Read RFCs for guidance on the expected record layer version number
@@ -545,7 +569,7 @@ public class DTLSServerProtocol
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
* to TRUE.
*/
- if (TlsProtocol.arrayContains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+ if (Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
{
state.secure_renegotiation = true;
}
@@ -554,23 +578,19 @@ public class DTLSServerProtocol
* The server MUST check if the "renegotiation_info" extension is included in the
* ClientHello.
*/
- if (state.clientExtensions != null)
+ byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo);
+ if (renegExtData != null)
{
- byte[] renegExtValue = (byte[])state.clientExtensions.get(TlsProtocol.EXT_RenegotiationInfo);
- if (renegExtValue != null)
- {
- /*
- * If the extension is present, set secure_renegotiation flag to TRUE. The
- * server MUST then verify that the length of the "renegotiated_connection"
- * field is zero, and if it is not, MUST abort the handshake.
- */
- state.secure_renegotiation = true;
+ /*
+ * If the extension is present, set secure_renegotiation flag to TRUE. The
+ * server MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake.
+ */
+ state.secure_renegotiation = true;
- if (!Arrays.constantTimeAreEqual(renegExtValue,
- TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
- {
- throw new TlsFatalAlert(AlertDescription.handshake_failure);
- }
+ if (!Arrays.constantTimeAreEqual(renegExtData, TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
}
}
@@ -586,20 +606,16 @@ public class DTLSServerProtocol
protected void processClientKeyExchange(ServerHandshakeState state, byte[] body)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
state.keyExchange.processClientKeyExchange(buf);
TlsProtocol.assertEmpty(buf);
-
- TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange);
}
protected void processClientSupplementalData(ServerHandshakeState state, byte[] body)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(body);
Vector clientSupplementalData = TlsProtocol.readSupplementalDataMessage(buf);
state.server.processClientSupplementalData(clientSupplementalData);
@@ -620,6 +636,8 @@ public class DTLSServerProtocol
int selectedCipherSuite = -1;
short selectedCompressionMethod = -1;
boolean secure_renegotiation = false;
+ short maxFragmentLength = -1;
+ boolean allowCertificateStatus = false;
boolean expectSessionTicket = false;
Hashtable serverExtensions = null;
TlsKeyExchange keyExchange = null;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java
index 98efc4f..78afb41 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java
@@ -11,9 +11,8 @@ import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.util.BigIntegers;
public class DefaultTlsAgreementCredentials
- implements TlsAgreementCredentials
+ extends AbstractTlsAgreementCredentials
{
-
protected Certificate certificate;
protected AsymmetricKeyParameter privateKey;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
index 82b37d9..7f70c64 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
@@ -4,6 +4,7 @@ import java.io.IOException;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
@@ -15,24 +16,37 @@ import org.bouncycastle.crypto.engines.CamelliaEngine;
import org.bouncycastle.crypto.engines.DESedeEngine;
import org.bouncycastle.crypto.engines.RC4Engine;
import org.bouncycastle.crypto.engines.SEEDEngine;
+import org.bouncycastle.crypto.engines.Salsa20Engine;
+import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CCMBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
public class DefaultTlsCipherFactory
extends AbstractTlsCipherFactory
{
-
public TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
throws IOException
{
-
switch (encryptionAlgorithm)
{
case EncryptionAlgorithm._3DES_EDE_CBC:
return createDESedeCipher(context, macAlgorithm);
case EncryptionAlgorithm.AES_128_CBC:
return createAESCipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.AES_128_CCM:
+ // NOTE: Ignores macAlgorithm
+ return createCipher_AES_CCM(context, 16, 16);
+ case EncryptionAlgorithm.AES_128_CCM_8:
+ // NOTE: Ignores macAlgorithm
+ return createCipher_AES_CCM(context, 16, 8);
+ case EncryptionAlgorithm.AES_256_CCM:
+ // NOTE: Ignores macAlgorithm
+ return createCipher_AES_CCM(context, 32, 16);
+ case EncryptionAlgorithm.AES_256_CCM_8:
+ // NOTE: Ignores macAlgorithm
+ return createCipher_AES_CCM(context, 32, 8);
case EncryptionAlgorithm.AES_128_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_GCM(context, 16, 16);
@@ -45,10 +59,14 @@ public class DefaultTlsCipherFactory
return createCamelliaCipher(context, 16, macAlgorithm);
case EncryptionAlgorithm.CAMELLIA_256_CBC:
return createCamelliaCipher(context, 32, macAlgorithm);
+ case EncryptionAlgorithm.ESTREAM_SALSA20:
+ return createSalsa20Cipher(context, 12, 32, macAlgorithm);
case EncryptionAlgorithm.NULL:
return createNullCipher(context, macAlgorithm);
case EncryptionAlgorithm.RC4_128:
return createRC4Cipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.SALSA20:
+ return createSalsa20Cipher(context, 20, 32, macAlgorithm);
case EncryptionAlgorithm.SEED_CBC:
return createSEEDCipher(context, macAlgorithm);
default:
@@ -63,6 +81,13 @@ public class DefaultTlsCipherFactory
createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
}
+ protected TlsAEADCipher createCipher_AES_CCM(TlsContext context, int cipherKeySize, int macSize)
+ throws IOException
+ {
+ return new TlsAEADCipher(context, createAEADBlockCipher_AES_CCM(),
+ createAEADBlockCipher_AES_CCM(), cipherKeySize, macSize);
+ }
+
protected TlsAEADCipher createCipher_AES_GCM(TlsContext context, int cipherKeySize, int macSize)
throws IOException
{
@@ -70,8 +95,7 @@ public class DefaultTlsCipherFactory
createAEADBlockCipher_AES_GCM(), cipherKeySize, macSize);
}
- protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize,
- int macAlgorithm)
+ protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
throws IOException
{
return new TlsBlockCipher(context, createCamelliaBlockCipher(),
@@ -79,6 +103,13 @@ public class DefaultTlsCipherFactory
createHMACDigest(macAlgorithm), cipherKeySize);
}
+ protected TlsBlockCipher createDESedeCipher(TlsContext context, int macAlgorithm)
+ throws IOException
+ {
+ return new TlsBlockCipher(context, createDESedeBlockCipher(), createDESedeBlockCipher(),
+ createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 24);
+ }
+
protected TlsNullCipher createNullCipher(TlsContext context, int macAlgorithm)
throws IOException
{
@@ -86,19 +117,22 @@ public class DefaultTlsCipherFactory
createHMACDigest(macAlgorithm));
}
- protected TlsStreamCipher createRC4Cipher(TlsContext context, int cipherKeySize,
- int macAlgorithm)
+ protected TlsStreamCipher createRC4Cipher(TlsContext context, int cipherKeySize, int macAlgorithm)
throws IOException
{
return new TlsStreamCipher(context, createRC4StreamCipher(), createRC4StreamCipher(),
createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
}
- protected TlsBlockCipher createDESedeCipher(TlsContext context, int macAlgorithm)
+ protected TlsStreamCipher createSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm)
throws IOException
{
- return new TlsBlockCipher(context, createDESedeBlockCipher(), createDESedeBlockCipher(),
- createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 24);
+ /*
+ * TODO To be able to support UMAC96, we need to give the TlsStreamCipher a Mac instead of
+ * assuming HMAC and passing a digest.
+ */
+ return new TlsStreamCipher(context, createSalsa20StreamCipher(rounds), createSalsa20StreamCipher(rounds),
+ createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
}
protected TlsBlockCipher createSEEDCipher(TlsContext context, int macAlgorithm)
@@ -108,14 +142,14 @@ public class DefaultTlsCipherFactory
createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 16);
}
- protected StreamCipher createRC4StreamCipher()
+ protected BlockCipher createAESBlockCipher()
{
- return new RC4Engine();
+ return new CBCBlockCipher(new AESFastEngine());
}
- protected BlockCipher createAESBlockCipher()
+ protected AEADBlockCipher createAEADBlockCipher_AES_CCM()
{
- return new CBCBlockCipher(new AESFastEngine());
+ return new CCMBlockCipher(new AESFastEngine());
}
protected AEADBlockCipher createAEADBlockCipher_AES_GCM()
@@ -134,13 +168,22 @@ public class DefaultTlsCipherFactory
return new CBCBlockCipher(new DESedeEngine());
}
+ protected StreamCipher createRC4StreamCipher()
+ {
+ return new RC4Engine();
+ }
+
+ protected StreamCipher createSalsa20StreamCipher(int rounds)
+ {
+ return new Salsa20Engine(rounds);
+ }
+
protected BlockCipher createSEEDBlockCipher()
{
return new CBCBlockCipher(new SEEDEngine());
}
- protected Digest createHMACDigest(int macAlgorithm)
- throws IOException
+ protected Digest createHMACDigest(int macAlgorithm) throws IOException
{
switch (macAlgorithm)
{
@@ -160,4 +203,16 @@ public class DefaultTlsCipherFactory
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
+
+ protected Mac createMac(int macAlgorithm) throws IOException
+ {
+ switch (macAlgorithm)
+ {
+ // TODO Need an implementation of UMAC
+// case MACAlgorithm.umac96:
+// return
+ default:
+ return new HMac(createHMACDigest(macAlgorithm));
+ }
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
index 4f9fe27..63db45f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
@@ -1,15 +1,10 @@
package org.bouncycastle.crypto.tls;
import java.io.IOException;
-import java.util.Hashtable;
public abstract class DefaultTlsClient
extends AbstractTlsClient
{
-
- protected int[] namedCurves;
- protected short[] clientECPointFormats, serverECPointFormats;
-
public DefaultTlsClient()
{
super();
@@ -22,75 +17,15 @@ public abstract class DefaultTlsClient
public int[] getCipherSuites()
{
- return new int[]{CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,};
- }
-
- public Hashtable getClientExtensions()
- throws IOException
- {
-
- Hashtable clientExtensions = super.getClientExtensions();
-
- if (TlsECCUtils.containsECCCipherSuites(getCipherSuites()))
- {
- /*
- * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message
- * appends these extensions (along with any others), enumerating the curves it supports
- * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic
- * Curves Extension and the Supported Point Formats Extension.
- */
- /*
- * TODO Could just add all the curves since we support them all, but users may not want
- * to use unnecessarily large fields. Need configuration options.
- */
- this.namedCurves = new int[]{NamedCurve.secp256r1, NamedCurve.sect233r1, NamedCurve.secp224r1,
- NamedCurve.sect193r1, NamedCurve.secp192r1, NamedCurve.arbitrary_explicit_char2_curves,
- NamedCurve.arbitrary_explicit_prime_curves};
- this.clientECPointFormats = new short[]{ECPointFormat.ansiX962_compressed_char2,
- ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed};
-
- if (clientExtensions == null)
- {
- clientExtensions = new Hashtable();
- }
-
- TlsECCUtils.addSupportedEllipticCurvesExtension(clientExtensions, namedCurves);
- TlsECCUtils.addSupportedPointFormatsExtension(clientExtensions, clientECPointFormats);
- }
-
- return clientExtensions;
- }
-
- public void processServerExtensions(Hashtable serverExtensions)
- throws IOException
- {
-
- super.processServerExtensions(serverExtensions);
-
- if (serverExtensions != null)
- {
- int[] namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(serverExtensions);
- if (namedCurves != null)
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
-
- this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions);
- if (this.serverECPointFormats != null && !TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
- }
+ return new int[] { CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA };
}
public TlsKeyExchange getKeyExchange()
throws IOException
{
-
switch (selectedCipherSuite)
{
case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -132,12 +67,20 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96:
case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
@@ -170,8 +113,12 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
@@ -181,24 +128,36 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96:
case CipherSuite.TLS_RSA_WITH_NULL_MD5:
case CipherSuite.TLS_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96:
case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
return createRSAKeyExchange();
@@ -215,7 +174,6 @@ public abstract class DefaultTlsClient
public TlsCipher getCipher()
throws IOException
{
-
switch (selectedCipherSuite)
{
case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -251,6 +209,14 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null);
+
case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
@@ -286,6 +252,14 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null);
+
case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
@@ -311,6 +285,18 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96);
+
case CipherSuite.TLS_RSA_WITH_NULL_MD5:
return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_md5);
@@ -334,6 +320,18 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96);
+
case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
index a338c38..dcdb493 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
@@ -10,14 +10,14 @@ import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
public class DefaultTlsEncryptionCredentials
- implements TlsEncryptionCredentials
+ extends AbstractTlsEncryptionCredentials
{
protected TlsContext context;
protected Certificate certificate;
protected AsymmetricKeyParameter privateKey;
public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate,
- AsymmetricKeyParameter privateKey)
+ AsymmetricKeyParameter privateKey)
{
if (certificate == null)
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
index 246b87e..31b6a74 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
@@ -48,15 +48,18 @@ public abstract class DefaultTlsServer
public TlsCredentials getCredentials()
throws IOException
{
-
switch (selectedCipherSuite)
{
case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
@@ -71,9 +74,13 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
@@ -85,6 +92,8 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
return getRSASignerCredentials();
default:
@@ -98,7 +107,6 @@ public abstract class DefaultTlsServer
public TlsKeyExchange getKeyExchange()
throws IOException
{
-
switch (selectedCipherSuite)
{
case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -140,12 +148,20 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96:
case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
@@ -178,8 +194,12 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
@@ -189,24 +209,36 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96:
case CipherSuite.TLS_RSA_WITH_NULL_MD5:
case CipherSuite.TLS_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96:
case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
return createRSAKeyExchange();
@@ -221,7 +253,6 @@ public abstract class DefaultTlsServer
public TlsCipher getCipher()
throws IOException
{
-
switch (selectedCipherSuite)
{
case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -257,6 +288,14 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null);
+
case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
@@ -292,6 +331,14 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null);
+
case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
@@ -317,6 +364,18 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96);
+
case CipherSuite.TLS_RSA_WITH_NULL_MD5:
return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_md5);
@@ -340,6 +399,18 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96);
+
case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
index b775250..15bf4a4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
@@ -9,17 +9,23 @@ import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
public class DefaultTlsSignerCredentials
- implements TlsSignerCredentials
+ extends AbstractTlsSignerCredentials
{
protected TlsContext context;
protected Certificate certificate;
protected AsymmetricKeyParameter privateKey;
+ protected SignatureAndHashAlgorithm signatureAndHashAlgorithm;
protected TlsSigner signer;
public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey)
{
+ this(context, certificate, privateKey, null);
+ }
+ public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey,
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
if (certificate == null)
{
throw new IllegalArgumentException("'certificate' cannot be null");
@@ -36,6 +42,10 @@ public class DefaultTlsSignerCredentials
{
throw new IllegalArgumentException("'privateKey' must be private");
}
+ if (TlsUtils.isTLSv12(context) && signatureAndHashAlgorithm == null)
+ {
+ throw new IllegalArgumentException("'signatureAndHashAlgorithm' cannot be null for (D)TLS 1.2+");
+ }
if (privateKey instanceof RSAKeyParameters)
{
@@ -59,6 +69,7 @@ public class DefaultTlsSignerCredentials
this.context = context;
this.certificate = certificate;
this.privateKey = privateKey;
+ this.signatureAndHashAlgorithm = signatureAndHashAlgorithm;
}
public Certificate getCertificate()
@@ -66,16 +77,28 @@ public class DefaultTlsSignerCredentials
return certificate;
}
- public byte[] generateCertificateSignature(byte[] md5andsha1)
+ public byte[] generateCertificateSignature(byte[] hash)
throws IOException
{
try
{
- return signer.generateRawSignature(privateKey, md5andsha1);
+ if (TlsUtils.isTLSv12(context))
+ {
+ return signer.generateRawSignature(signatureAndHashAlgorithm, privateKey, hash);
+ }
+ else
+ {
+ return signer.generateRawSignature(privateKey, hash);
+ }
}
catch (CryptoException e)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
+
+ public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm()
+ {
+ return signatureAndHashAlgorithm;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java
index e8c76e6..274e69a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java
@@ -1,8 +1,10 @@
package org.bouncycastle.crypto.tls;
-import java.io.ByteArrayOutputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Shorts;
/**
* Buffers input until the hash algorithm is determined.
@@ -10,23 +12,27 @@ import org.bouncycastle.crypto.Digest;
class DeferredHash
implements TlsHandshakeHash
{
+ protected static final int BUFFERING_HASH_LIMIT = 4;
protected TlsContext context;
- private ByteArrayOutputStream buf = new ByteArrayOutputStream();
- private int prfAlgorithm = -1;
- private Digest hash = null;
+ private DigestInputBuffer buf;
+ private Hashtable hashes;
+ private Short prfHashAlgorithm;
DeferredHash()
{
- this.buf = new ByteArrayOutputStream();
- this.hash = null;
+ this.buf = new DigestInputBuffer();
+ this.hashes = new Hashtable();
+ this.prfHashAlgorithm = null;
}
- private DeferredHash(Digest hash)
+ private DeferredHash(Short prfHashAlgorithm, Digest prfHash)
{
this.buf = null;
- this.hash = hash;
+ this.hashes = new Hashtable();
+ this.prfHashAlgorithm = prfHashAlgorithm;
+ hashes.put(prfHashAlgorithm, prfHash);
}
public void init(TlsContext context)
@@ -34,95 +40,168 @@ class DeferredHash
this.context = context;
}
- public TlsHandshakeHash commit()
+ public TlsHandshakeHash notifyPRFDetermined()
{
-
int prfAlgorithm = context.getSecurityParameters().getPrfAlgorithm();
+ if (prfAlgorithm == PRFAlgorithm.tls_prf_legacy)
+ {
+ CombinedHash legacyHash = new CombinedHash();
+ legacyHash.init(context);
+ buf.updateDigest(legacyHash);
+ return legacyHash.notifyPRFDetermined();
+ }
- Digest prfHash = TlsUtils.createPRFHash(prfAlgorithm);
+ this.prfHashAlgorithm = Shorts.valueOf(TlsUtils.getHashAlgorithmForPRFAlgorithm(prfAlgorithm));
- byte[] data = buf.toByteArray();
- prfHash.update(data, 0, data.length);
+ checkTrackingHash(prfHashAlgorithm);
+
+ return this;
+ }
- if (prfHash instanceof TlsHandshakeHash)
+ public void trackHashAlgorithm(short hashAlgorithm)
+ {
+ if (buf == null)
{
- TlsHandshakeHash tlsPRFHash = (TlsHandshakeHash)prfHash;
- tlsPRFHash.init(context);
- return tlsPRFHash.commit();
+ throw new IllegalStateException("Too late to track more hash algorithms");
}
- this.prfAlgorithm = prfAlgorithm;
- this.hash = prfHash;
- this.buf = null;
+ checkTrackingHash(Shorts.valueOf(hashAlgorithm));
+ }
- return this;
+ public void sealHashAlgorithms()
+ {
+ checkStopBuffering();
}
- public TlsHandshakeHash fork()
+ public TlsHandshakeHash stopTracking()
{
- checkHash();
- return new DeferredHash(TlsUtils.clonePRFHash(prfAlgorithm, hash));
+ Digest prfHash = TlsUtils.cloneHash(prfHashAlgorithm.shortValue(), (Digest)hashes.get(prfHashAlgorithm));
+ if (buf != null)
+ {
+ buf.updateDigest(prfHash);
+ }
+ DeferredHash result = new DeferredHash(prfHashAlgorithm, prfHash);
+ result.init(context);
+ return result;
+ }
+
+ public Digest forkPRFHash()
+ {
+ checkStopBuffering();
+
+ if (buf != null)
+ {
+ Digest prfHash = TlsUtils.createHash(prfHashAlgorithm.shortValue());
+ buf.updateDigest(prfHash);
+ return prfHash;
+ }
+
+ return TlsUtils.cloneHash(prfHashAlgorithm.shortValue(), (Digest)hashes.get(prfHashAlgorithm));
+ }
+
+ public byte[] getFinalHash(short hashAlgorithm)
+ {
+ Digest d = (Digest)hashes.get(Shorts.valueOf(hashAlgorithm));
+ if (d == null)
+ {
+ throw new IllegalStateException("HashAlgorithm " + hashAlgorithm + " is not being tracked");
+ }
+
+ d = TlsUtils.cloneHash(hashAlgorithm, d);
+ if (buf != null)
+ {
+ buf.updateDigest(d);
+ }
+
+ byte[] bs = new byte[d.getDigestSize()];
+ d.doFinal(bs, 0);
+ return bs;
}
public String getAlgorithmName()
{
- checkHash();
- return hash.getAlgorithmName();
+ throw new IllegalStateException("Use fork() to get a definite Digest");
}
public int getDigestSize()
{
- checkHash();
- return hash.getDigestSize();
+ throw new IllegalStateException("Use fork() to get a definite Digest");
}
public void update(byte input)
{
- if (hash == null)
+ if (buf != null)
{
buf.write(input);
+ return;
}
- else
+
+ Enumeration e = hashes.elements();
+ while (e.hasMoreElements())
{
+ Digest hash = (Digest)e.nextElement();
hash.update(input);
}
}
public void update(byte[] input, int inOff, int len)
{
- if (hash == null)
+ if (buf != null)
{
buf.write(input, inOff, len);
+ return;
}
- else
+
+ Enumeration e = hashes.elements();
+ while (e.hasMoreElements())
{
+ Digest hash = (Digest)e.nextElement();
hash.update(input, inOff, len);
}
}
public int doFinal(byte[] output, int outOff)
{
- checkHash();
- return hash.doFinal(output, outOff);
+ throw new IllegalStateException("Use fork() to get a definite Digest");
}
public void reset()
{
- if (hash == null)
+ if (buf != null)
{
buf.reset();
+ return;
}
- else
+
+ Enumeration e = hashes.elements();
+ while (e.hasMoreElements())
{
+ Digest hash = (Digest)e.nextElement();
hash.reset();
}
}
- protected void checkHash()
+ protected void checkStopBuffering()
+ {
+ if (buf != null && hashes.size() <= BUFFERING_HASH_LIMIT)
+ {
+ Enumeration e = hashes.elements();
+ while (e.hasMoreElements())
+ {
+ Digest hash = (Digest)e.nextElement();
+ buf.updateDigest(hash);
+ }
+
+ this.buf = null;
+ }
+ }
+
+ protected void checkTrackingHash(Short hashAlgorithm)
{
- if (hash == null)
+ if (!hashes.containsKey(hashAlgorithm))
{
- throw new IllegalStateException("No hash algorithm has been set");
+ Digest hash = TlsUtils.createHash(hashAlgorithm.shortValue());
+ hashes.put(hashAlgorithm, hash);
}
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java
new file mode 100644
index 0000000..7cfa890
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.Digest;
+
+class DigestInputBuffer extends ByteArrayOutputStream
+{
+ void updateDigest(Digest d)
+ {
+ d.update(this.buf, 0, count);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java
new file mode 100644
index 0000000..c366ca9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class DigitallySigned
+{
+ protected SignatureAndHashAlgorithm algorithm;
+ protected byte[] signature;
+
+ public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature)
+ {
+ if (signature == null)
+ {
+ throw new IllegalArgumentException("'signature' cannot be null");
+ }
+
+ this.algorithm = algorithm;
+ this.signature = signature;
+ }
+
+ /**
+ * @return a {@link SignatureAndHashAlgorithm} (or null before TLS 1.2).
+ */
+ public SignatureAndHashAlgorithm getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ public byte[] getSignature()
+ {
+ return signature;
+ }
+
+ /**
+ * Encode this {@link DigitallySigned} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ if (algorithm != null)
+ {
+ algorithm.encode(output);
+ }
+ TlsUtils.writeOpaque16(signature, output);
+ }
+
+ /**
+ * Parse a {@link DigitallySigned} from an {@link InputStream}.
+ *
+ * @param context
+ * the {@link TlsContext} of the current connection.
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link DigitallySigned} object.
+ * @throws IOException
+ */
+ public static DigitallySigned parse(TlsContext context, InputStream input) throws IOException
+ {
+ SignatureAndHashAlgorithm algorithm = null;
+ if (TlsUtils.isTLSv12(context))
+ {
+ algorithm = SignatureAndHashAlgorithm.parse(input);
+ }
+ byte[] signature = TlsUtils.readOpaque16(input);
+ return new DigitallySigned(algorithm, signature);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
index f991e4a..6f3fe01 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
@@ -25,6 +25,12 @@ public class EncryptionAlgorithm
public static final int AES_256_CBC = 9;
/*
+ * RFC 5289
+ */
+ public static final int AES_128_GCM = 10;
+ public static final int AES_256_GCM = 11;
+
+ /*
* RFC 4132
*/
public static final int CAMELLIA_128_CBC = 12;
@@ -36,8 +42,16 @@ public class EncryptionAlgorithm
public static final int SEED_CBC = 14;
/*
- * RFC 5289
+ * RFC 6655
*/
- public static final int AES_128_GCM = 10;
- public static final int AES_256_GCM = 11;
+ public static final int AES_128_CCM = 15;
+ public static final int AES_128_CCM_8 = 16;
+ public static final int AES_256_CCM = 17;
+ public static final int AES_256_CCM_8 = 18;
+
+ /*
+ * TBD[draft-josefsson-salsa20-tls-02]
+ */
+ static final int ESTREAM_SALSA20 = 100;
+ static final int SALSA20 = 101;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
index 0be6465..8312e93 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
@@ -3,7 +3,7 @@ package org.bouncycastle.crypto.tls;
public class ExtensionType
{
/*
- * RFC 6066 1.1.
+ * RFC 2546 2.3.
*/
public static final int server_name = 0;
public static final int max_fragment_length = 1;
@@ -44,6 +44,11 @@ public class ExtensionType
public static final int use_srtp = 14;
/*
+ * RFC 6520 6.
+ */
+ public static final int heartbeat = 15;
+
+ /*
* RFC 5746 3.2.
*/
public static final int renegotiation_info = 0xff01;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java
index 53b4520..c81660a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java
@@ -17,6 +17,12 @@ public class HandshakeType
public static final short finished = 20;
/*
+ * RFC 3546 2.4
+ */
+ public static final short certificate_url = 21;
+ public static final short certificate_status = 22;
+
+ /*
* (DTLS) RFC 4347 4.3.2
*/
public static final short hello_verify_request = 3;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
index ac0a4c6..dc7482b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
@@ -5,7 +5,6 @@ package org.bouncycastle.crypto.tls;
*/
public class HashAlgorithm
{
-
public static final short none = 0;
public static final short md5 = 1;
public static final short sha1 = 2;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java
new file mode 100644
index 0000000..f9f3670
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class HeartbeatExtension
+{
+ protected short mode;
+
+ public HeartbeatExtension(short mode)
+ {
+ if (!HeartbeatMode.isValid(mode))
+ {
+ throw new IllegalArgumentException("'mode' is not a valid HeartbeatMode value");
+ }
+
+ this.mode = mode;
+ }
+
+ public short getMode()
+ {
+ return mode;
+ }
+
+ /**
+ * Encode this {@link HeartbeatExtension} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ TlsUtils.writeUint8(mode, output);
+ }
+
+ /**
+ * Parse a {@link HeartbeatExtension} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link HeartbeatExtension} object.
+ * @throws IOException
+ */
+ public static HeartbeatExtension parse(InputStream input) throws IOException
+ {
+ short mode = TlsUtils.readUint8(input);
+ if (!HeartbeatMode.isValid(mode))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return new HeartbeatExtension(mode);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java
new file mode 100644
index 0000000..320a128
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public class HeartbeatMessage
+{
+ protected short type;
+ protected byte[] payload;
+ protected int paddingLength;
+
+ public HeartbeatMessage(short type, byte[] payload, int paddingLength)
+ {
+ if (!HeartbeatMessageType.isValid(type))
+ {
+ throw new IllegalArgumentException("'type' is not a valid HeartbeatMessageType value");
+ }
+ if (payload == null || payload.length >= (1 << 16))
+ {
+ throw new IllegalArgumentException("'payload' must have length < 2^16");
+ }
+ if (paddingLength < 16)
+ {
+ throw new IllegalArgumentException("'paddingLength' must be at least 16");
+ }
+
+ this.type = type;
+ this.payload = payload;
+ this.paddingLength = paddingLength;
+ }
+
+ /**
+ * Encode this {@link HeartbeatMessage} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(TlsContext context, OutputStream output) throws IOException
+ {
+ TlsUtils.writeUint8(type, output);
+
+ TlsUtils.checkUint16(payload.length);
+ TlsUtils.writeUint16(payload.length, output);
+ output.write(payload);
+
+ byte[] padding = new byte[paddingLength];
+ context.getSecureRandom().nextBytes(padding);
+ output.write(padding);
+ }
+
+ /**
+ * Parse a {@link HeartbeatMessage} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link HeartbeatMessage} object.
+ * @throws IOException
+ */
+ public static HeartbeatMessage parse(InputStream input) throws IOException
+ {
+ short type = TlsUtils.readUint8(input);
+ if (!HeartbeatMessageType.isValid(type))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ int payload_length = TlsUtils.readUint16(input);
+
+ PayloadBuffer buf = new PayloadBuffer();
+ Streams.pipeAll(input, buf);
+
+ byte[] payload = buf.toTruncatedByteArray(payload_length);
+ if (payload == null)
+ {
+ /*
+ * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the
+ * received HeartbeatMessage MUST be discarded silently.
+ */
+ return null;
+ }
+
+ int padding_length = buf.size() - payload.length;
+
+ return new HeartbeatMessage(type, payload, padding_length);
+ }
+
+ static class PayloadBuffer extends ByteArrayOutputStream
+ {
+ byte[] toTruncatedByteArray(int payloadLength)
+ {
+ /*
+ * RFC 6520 4. The padding_length MUST be at least 16.
+ */
+ int minimumCount = payloadLength + 16;
+ if (count < minimumCount)
+ {
+ return null;
+ }
+ return Arrays.copyOf(buf, payloadLength);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java
new file mode 100644
index 0000000..f1a3b43
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+/*
+ * RFC 6520 3.
+ */
+public class HeartbeatMessageType
+{
+ public static final short heartbeat_request = 1;
+ public static final short heartbeat_response = 2;
+
+ public static boolean isValid(short heartbeatMessageType)
+ {
+ return heartbeatMessageType >= heartbeat_request && heartbeatMessageType <= heartbeat_response;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java
new file mode 100644
index 0000000..4024faa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+/*
+ * RFC 6520
+ */
+public class HeartbeatMode
+{
+ public static final short peer_allowed_to_send = 1;
+ public static final short peer_not_allowed_to_send = 2;
+
+ public static boolean isValid(short heartbeatMode)
+ {
+ return heartbeatMode >= peer_allowed_to_send && heartbeatMode <= peer_not_allowed_to_send;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
index c049bb7..72a944f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
@@ -44,4 +44,9 @@ public class KeyExchangeAlgorithm
public static final int SRP = 21;
public static final int SRP_DSS = 22;
public static final int SRP_RSA = 23;
+
+ /*
+ * RFC 5489
+ */
+ public static final int ECDHE_PSK = 24;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
index 40ef15c..92adc8c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
@@ -8,7 +8,6 @@ package org.bouncycastle.crypto.tls;
*/
public class MACAlgorithm
{
-
public static final int _null = 0;
public static final int md5 = 1;
public static final int sha = 2;
@@ -21,4 +20,9 @@ public class MACAlgorithm
public static final int hmac_sha256 = 3;
public static final int hmac_sha384 = 4;
public static final int hmac_sha512 = 5;
+
+ /*
+ * TBD[draft-josefsson-salsa20-tls-02]
+ */
+ static final int umac96 = 100;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java
new file mode 100644
index 0000000..413695c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.tls;
+
+public class MaxFragmentLength
+{
+ /*
+ * RFC 3546 3.2.
+ */
+ public static short pow2_9 = 1;
+ public static short pow2_10 = 2;
+ public static short pow2_11 = 3;
+ public static short pow2_12 = 4;
+
+ public static boolean isValid(short maxFragmentLength)
+ {
+ return maxFragmentLength >= pow2_9 && maxFragmentLength <= pow2_12;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java
new file mode 100644
index 0000000..9b0cd1b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.crypto.tls;
+
+public class NameType
+{
+ /*
+ * RFC 3546 3.1.
+ */
+ public static final short host_name = 0;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
index 690115c..a965d13 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
@@ -36,6 +36,13 @@ public class NamedCurve
public static final int secp256r1 = 23;
public static final int secp384r1 = 24;
public static final int secp521r1 = 25;
+
+ /*
+ * RFC 7027
+ */
+ public static final int brainpoolP256r1 = 26;
+ public static final int brainpoolP384r1 = 27;
+ public static final int brainpoolP512r1 = 28;
/*
* reserved (0xFE00..0xFEFF)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java
index f3d1022..8f87a65 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java
@@ -6,7 +6,6 @@ import java.io.OutputStream;
public class NewSessionTicket
{
-
protected long ticketLifetimeHint;
protected byte[] ticket;
@@ -26,6 +25,12 @@ public class NewSessionTicket
return ticket;
}
+ /**
+ * Encode this {@link NewSessionTicket} to an {@link OutputStream}.
+ *
+ * @param output the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
public void encode(OutputStream output)
throws IOException
{
@@ -33,6 +38,13 @@ public class NewSessionTicket
TlsUtils.writeOpaque16(ticket, output);
}
+ /**
+ * Parse a {@link NewSessionTicket} from an {@link InputStream}.
+ *
+ * @param input the {@link InputStream} to parse from.
+ * @return a {@link NewSessionTicket} object.
+ * @throws IOException
+ */
public static NewSessionTicket parse(InputStream input)
throws IOException
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java
new file mode 100644
index 0000000..db8168f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ocsp.ResponderID;
+import org.bouncycastle.asn1.x509.Extensions;
+
+/**
+ * RFC 3546 3.6
+ */
+public class OCSPStatusRequest
+{
+ protected Vector responderIDList;
+ protected Extensions requestExtensions;
+
+ /**
+ * @param responderIDList
+ * a {@link Vector} of {@link ResponderID}, specifying the list of trusted OCSP
+ * responders. An empty list has the special meaning that the responders are
+ * implicitly known to the server - e.g., by prior arrangement.
+ * @param requestExtensions
+ * OCSP request extensions. A null value means that there are no extensions.
+ */
+ public OCSPStatusRequest(Vector responderIDList, Extensions requestExtensions)
+ {
+ this.responderIDList = responderIDList;
+ this.requestExtensions = requestExtensions;
+ }
+
+ /**
+ * @return a {@link Vector} of {@link ResponderID}
+ */
+ public Vector getResponderIDList()
+ {
+ return responderIDList;
+ }
+
+ /**
+ * @return OCSP request extensions
+ */
+ public Extensions getRequestExtensions()
+ {
+ return requestExtensions;
+ }
+
+ /**
+ * Encode this {@link OCSPStatusRequest} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ if (responderIDList == null || responderIDList.isEmpty())
+ {
+ TlsUtils.writeUint16(0, output);
+ }
+ else
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ for (int i = 0; i < responderIDList.size(); ++i)
+ {
+ ResponderID responderID = (ResponderID) responderIDList.elementAt(i);
+ byte[] derEncoding = responderID.getEncoded(ASN1Encoding.DER);
+ TlsUtils.writeOpaque16(derEncoding, buf);
+ }
+ TlsUtils.checkUint16(buf.size());
+ TlsUtils.writeUint16(buf.size(), output);
+ buf.writeTo(output);
+ }
+
+ if (requestExtensions == null)
+ {
+ TlsUtils.writeUint16(0, output);
+ }
+ else
+ {
+ byte[] derEncoding = requestExtensions.getEncoded(ASN1Encoding.DER);
+ TlsUtils.checkUint16(derEncoding.length);
+ TlsUtils.writeUint16(derEncoding.length, output);
+ output.write(derEncoding);
+ }
+ }
+
+ /**
+ * Parse a {@link OCSPStatusRequest} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link OCSPStatusRequest} object.
+ * @throws IOException
+ */
+ public static OCSPStatusRequest parse(InputStream input) throws IOException
+ {
+ Vector responderIDList = new Vector();
+ {
+ int length = TlsUtils.readUint16(input);
+ if (length > 0)
+ {
+ byte[] data = TlsUtils.readFully(length, input);
+ ByteArrayInputStream buf = new ByteArrayInputStream(data);
+ do
+ {
+ byte[] derEncoding = TlsUtils.readOpaque16(buf);
+ ResponderID responderID = ResponderID.getInstance(TlsUtils.readDERObject(derEncoding));
+ responderIDList.addElement(responderID);
+ }
+ while (buf.available() > 0);
+ }
+ }
+
+ Extensions requestExtensions = null;
+ {
+ int length = TlsUtils.readUint16(input);
+ if (length > 0)
+ {
+ byte[] derEncoding = TlsUtils.readFully(length, input);
+ requestExtensions = Extensions.getInstance(TlsUtils.readDERObject(derEncoding));
+ }
+ }
+
+ return new OCSPStatusRequest(responderIDList, requestExtensions);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
index 29750cb..92475b2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
@@ -21,41 +21,89 @@ public abstract class PSKTlsClient
public int[] getCipherSuites()
{
- return new int[]{CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA,
- CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA,
- CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_PSK_WITH_RC4_128_SHA,};
+ return new int[] { CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA };
}
- public TlsKeyExchange getKeyExchange()
- throws IOException
+ public TlsKeyExchange getKeyExchange() throws IOException
{
-
switch (selectedCipherSuite)
{
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+ return createPSKKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+ case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96:
+ return createPSKKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK);
+
case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96:
case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_SALSA20_UMAC96:
return createPSKKeyExchange(KeyExchangeAlgorithm.PSK);
case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96:
case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_UMAC96:
return createPSKKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
- case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
- return createPSKKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
-
default:
/*
* Note: internal error here; the TlsProtocol implementation verifies that the
@@ -66,37 +114,114 @@ public abstract class PSKTlsClient
}
}
- public TlsCipher getCipher()
- throws IOException
+ public TlsCipher getCipher() throws IOException
{
-
switch (selectedCipherSuite)
{
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
case CipherSuite.TLS_PSK_WITH_NULL_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_PSK_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_UMAC96:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96);
+
default:
/*
* Note: internal error here; the TlsProtocol implementation verifies that the
@@ -109,6 +234,7 @@ public abstract class PSKTlsClient
protected TlsKeyExchange createPSKKeyExchange(int keyExchange)
{
- return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity);
+ return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity, null, namedCurves,
+ clientECPointFormats, serverECPointFormats);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
index c001e58..b32bd9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
@@ -4,7 +4,6 @@ import java.io.IOException;
public final class ProtocolVersion
{
-
public static final ProtocolVersion SSLv3 = new ProtocolVersion(0x0300, "SSL 3.0");
public static final ProtocolVersion TLSv10 = new ProtocolVersion(0x0301, "TLS 1.0");
public static final ProtocolVersion TLSv11 = new ProtocolVersion(0x0302, "TLS 1.1");
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
index 3a31c20..cc6640b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
@@ -12,10 +12,7 @@ import org.bouncycastle.crypto.Digest;
*/
class RecordStream
{
-
- private static int PLAINTEXT_LIMIT = (1 << 14);
- private static int COMPRESSED_LIMIT = PLAINTEXT_LIMIT + 1024;
- private static int CIPHERTEXT_LIMIT = COMPRESSED_LIMIT + 1024;
+ private static int DEFAULT_PLAINTEXT_LIMIT = (1 << 14);
private TlsProtocol handler;
private InputStream input;
@@ -26,11 +23,13 @@ class RecordStream
private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private TlsContext context = null;
- private TlsHandshakeHash hash = null;
+ private TlsHandshakeHash handshakeHash = null;
private ProtocolVersion readVersion = null, writeVersion = null;
private boolean restrictReadVersion = true;
+ private int plaintextLimit, compressedLimit, ciphertextLimit;
+
RecordStream(TlsProtocol handler, InputStream input, OutputStream output)
{
this.handler = handler;
@@ -40,13 +39,27 @@ class RecordStream
this.writeCompression = this.readCompression;
this.readCipher = new TlsNullCipher(context);
this.writeCipher = this.readCipher;
+
+ setPlaintextLimit(DEFAULT_PLAINTEXT_LIMIT);
}
void init(TlsContext context)
{
this.context = context;
- this.hash = new DeferredHash();
- this.hash.init(context);
+ this.handshakeHash = new DeferredHash();
+ this.handshakeHash.init(context);
+ }
+
+ int getPlaintextLimit()
+ {
+ return plaintextLimit;
+ }
+
+ void setPlaintextLimit(int plaintextLimit)
+ {
+ this.plaintextLimit = plaintextLimit;
+ this.compressedLimit = this.plaintextLimit + 1024;
+ this.ciphertextLimit = this.compressedLimit + 1024;
}
ProtocolVersion getReadVersion()
@@ -76,11 +89,6 @@ class RecordStream
this.restrictReadVersion = enabled;
}
- void notifyHelloComplete()
- {
- this.hash = this.hash.commit();
- }
-
void setPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher)
{
this.pendingCompression = tlsCompression;
@@ -123,13 +131,17 @@ class RecordStream
pendingCipher = null;
}
- public void readRecord()
+ public boolean readRecord()
throws IOException
{
+ byte[] recordHeader = TlsUtils.readAllOrNothing(5, input);
+ if (recordHeader == null)
+ {
+ return false;
+ }
- short type = TlsUtils.readUint8(input);
+ short type = TlsUtils.readUint8(recordHeader, 0);
- // TODO In earlier RFCs, it was "SHOULD ignore"; should this be version-dependent?
/*
* RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an
* unexpected_message alert.
@@ -138,7 +150,7 @@ class RecordStream
if (!restrictReadVersion)
{
- int version = TlsUtils.readVersionRaw(input);
+ int version = TlsUtils.readVersionRaw(recordHeader, 1);
if ((version & 0xffffff00) != 0x0300)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
@@ -146,7 +158,7 @@ class RecordStream
}
else
{
- ProtocolVersion version = TlsUtils.readVersion(input);
+ ProtocolVersion version = TlsUtils.readVersion(recordHeader, 1);
if (readVersion == null)
{
readVersion = version;
@@ -157,21 +169,21 @@ class RecordStream
}
}
- int length = TlsUtils.readUint16(input);
+ int length = TlsUtils.readUint16(recordHeader, 3);
byte[] plaintext = decodeAndVerify(type, input, length);
handler.processRecord(type, plaintext, 0, plaintext.length);
+ return true;
}
protected byte[] decodeAndVerify(short type, InputStream input, int len)
throws IOException
{
-
- checkLength(len, CIPHERTEXT_LIMIT, AlertDescription.record_overflow);
+ checkLength(len, ciphertextLimit, AlertDescription.record_overflow);
byte[] buf = TlsUtils.readFully(len, input);
byte[] decoded = readCipher.decodeCiphertext(readSeqNo++, type, buf, 0, buf.length);
- checkLength(decoded.length, COMPRESSED_LIMIT, AlertDescription.record_overflow);
+ checkLength(decoded.length, compressedLimit, AlertDescription.record_overflow);
/*
* TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for
@@ -190,7 +202,16 @@ class RecordStream
* would decompress to a length in excess of 2^14 bytes, it should report a fatal
* decompression failure error.
*/
- checkLength(decoded.length, PLAINTEXT_LIMIT, AlertDescription.decompression_failure);
+ checkLength(decoded.length, plaintextLimit, AlertDescription.decompression_failure);
+
+ /*
+ * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (decoded.length < 1 && type != ContentType.application_data)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
return decoded;
}
@@ -198,7 +219,6 @@ class RecordStream
protected void writeRecord(short type, byte[] plaintext, int plaintextOffset, int plaintextLength)
throws IOException
{
-
/*
* RFC 5264 6. Implementations MUST NOT send record types not defined in this document
* unless negotiated by some extension.
@@ -208,7 +228,7 @@ class RecordStream
/*
* RFC 5264 6.2.1 The length should not exceed 2^14.
*/
- checkLength(plaintextLength, PLAINTEXT_LIMIT, AlertDescription.internal_error);
+ checkLength(plaintextLength, plaintextLimit, AlertDescription.internal_error);
/*
* RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
@@ -249,7 +269,7 @@ class RecordStream
/*
* RFC 5264 6.2.3. The length may not exceed 2^14 + 2048.
*/
- checkLength(ciphertext.length, CIPHERTEXT_LIMIT, AlertDescription.internal_error);
+ checkLength(ciphertext.length, ciphertextLimit, AlertDescription.internal_error);
byte[] record = new byte[ciphertext.length + 5];
TlsUtils.writeUint8(type, record, 0);
@@ -260,52 +280,44 @@ class RecordStream
output.flush();
}
- void updateHandshakeData(byte[] message, int offset, int len)
+ void notifyHelloComplete()
{
- hash.update(message, offset, len);
+ this.handshakeHash = handshakeHash.notifyPRFDetermined();
}
- /**
- * 'sender' only relevant to SSLv3
- */
- byte[] getCurrentHash(byte[] sender)
+ TlsHandshakeHash getHandshakeHash()
{
- TlsHandshakeHash d = hash.fork();
+ return handshakeHash;
+ }
- if (context.getServerVersion().isSSL())
- {
- if (sender != null)
- {
- d.update(sender, 0, sender.length);
- }
- }
+ TlsHandshakeHash prepareToFinish()
+ {
+ TlsHandshakeHash result = handshakeHash;
+ this.handshakeHash = handshakeHash.stopTracking();
+ return result;
+ }
- return doFinal(d);
+ void updateHandshakeData(byte[] message, int offset, int len)
+ {
+ handshakeHash.update(message, offset, len);
}
- protected void close()
- throws IOException
+ protected void safeClose()
{
- IOException e = null;
try
{
input.close();
}
- catch (IOException ex)
+ catch (IOException e)
{
- e = ex;
}
+
try
{
output.close();
}
- catch (IOException ex)
- {
- e = ex;
- }
- if (e != null)
+ catch (IOException e)
{
- throw e;
}
}
@@ -322,23 +334,16 @@ class RecordStream
return contents;
}
- private static byte[] doFinal(Digest d)
- {
- byte[] bs = new byte[d.getDigestSize()];
- d.doFinal(bs, 0);
- return bs;
- }
-
private static void checkType(short type, short alertDescription)
throws IOException
{
-
switch (type)
{
- case ContentType.change_cipher_spec:
+ case ContentType.application_data:
case ContentType.alert:
+ case ContentType.change_cipher_spec:
case ContentType.handshake:
- case ContentType.application_data:
+ case ContentType.heartbeat:
break;
default:
throw new TlsFatalAlert(alertDescription);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
index a5d4840..15295ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
@@ -1,16 +1,17 @@
package org.bouncycastle.crypto.tls;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Hashtable;
import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Integers;
public abstract class SRPTlsClient
extends AbstractTlsClient
{
- public static final Integer EXT_SRP = Integers.valueOf(ExtensionType.srp);
+ /**
+ * @deprecated use TlsSRPUtils.EXT_SRP instead
+ */
+ public static final Integer EXT_SRP = TlsSRPUtils.EXT_SRP;
protected byte[] identity;
protected byte[] password;
@@ -31,40 +32,23 @@ public abstract class SRPTlsClient
public int[] getCipherSuites()
{
- return new int[]{CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,};
+ return new int[] { CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA };
}
public Hashtable getClientExtensions()
throws IOException
{
-
- Hashtable clientExtensions = super.getClientExtensions();
- if (clientExtensions == null)
- {
- clientExtensions = new Hashtable();
- }
-
- ByteArrayOutputStream srpData = new ByteArrayOutputStream();
- TlsUtils.writeOpaque8(this.identity, srpData);
- clientExtensions.put(EXT_SRP, srpData.toByteArray());
-
+ Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
+ TlsSRPUtils.addSRPExtension(clientExtensions, this.identity);
return clientExtensions;
}
public void processServerExtensions(Hashtable serverExtensions)
throws IOException
{
- // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
- if (serverExtensions != null)
+ if (!TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, TlsSRPUtils.EXT_SRP, AlertDescription.illegal_parameter))
{
- byte[] extValue = (byte[])serverExtensions.get(EXT_SRP);
- if (extValue != null && extValue.length > 0)
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
+ // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
index a7701fe..984246e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
@@ -1,16 +1,41 @@
package org.bouncycastle.crypto.tls;
+import org.bouncycastle.util.Arrays;
+
public class SecurityParameters
{
-
int entity = -1;
- int prfAlgorithm = -1;
+ int cipherSuite = -1;
short compressionAlgorithm = -1;
+ int prfAlgorithm = -1;
int verifyDataLength = -1;
byte[] masterSecret = null;
byte[] clientRandom = null;
byte[] serverRandom = null;
+ // TODO Keep these internal, since it's maybe not the ideal place for them
+ short maxFragmentLength = -1;
+ boolean truncatedHMac = false;
+
+ void copySessionParametersFrom(SecurityParameters other)
+ {
+ this.entity = other.entity;
+ this.cipherSuite = other.cipherSuite;
+ this.compressionAlgorithm = other.compressionAlgorithm;
+ this.prfAlgorithm = other.prfAlgorithm;
+ this.verifyDataLength = other.verifyDataLength;
+ this.masterSecret = Arrays.clone(other.masterSecret);
+ }
+
+ void clear()
+ {
+ if (this.masterSecret != null)
+ {
+ Arrays.fill(this.masterSecret, (byte)0);
+ this.masterSecret = null;
+ }
+ }
+
/**
* @return {@link ConnectionEnd}
*/
@@ -20,11 +45,11 @@ public class SecurityParameters
}
/**
- * @return {@link PRFAlgorithm}
+ * @return {@link CipherSuite}
*/
- public int getPrfAlgorithm()
+ public int getCipherSuite()
{
- return prfAlgorithm;
+ return cipherSuite;
}
/**
@@ -35,6 +60,14 @@ public class SecurityParameters
return compressionAlgorithm;
}
+ /**
+ * @return {@link PRFAlgorithm}
+ */
+ public int getPrfAlgorithm()
+ {
+ return prfAlgorithm;
+ }
+
public int getVerifyDataLength()
{
return verifyDataLength;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
new file mode 100644
index 0000000..c3050f1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+
+public class ServerDHParams
+{
+ protected DHPublicKeyParameters publicKey;
+
+ public ServerDHParams(DHPublicKeyParameters publicKey)
+ {
+ if (publicKey == null)
+ {
+ throw new IllegalArgumentException("'publicKey' cannot be null");
+ }
+
+ this.publicKey = publicKey;
+ }
+
+ public DHPublicKeyParameters getPublicKey()
+ {
+ return publicKey;
+ }
+
+ /**
+ * Encode this {@link ServerDHParams} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ DHParameters dhParameters = publicKey.getParameters();
+ BigInteger Ys = publicKey.getY();
+
+ TlsDHUtils.writeDHParameter(dhParameters.getP(), output);
+ TlsDHUtils.writeDHParameter(dhParameters.getG(), output);
+ TlsDHUtils.writeDHParameter(Ys, output);
+ }
+
+ /**
+ * Parse a {@link ServerDHParams} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link ServerDHParams} object.
+ * @throws IOException
+ */
+ public static ServerDHParams parse(InputStream input) throws IOException
+ {
+ BigInteger p = TlsDHUtils.readDHParameter(input);
+ BigInteger g = TlsDHUtils.readDHParameter(input);
+ BigInteger Ys = TlsDHUtils.readDHParameter(input);
+
+ return new ServerDHParams(new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java
new file mode 100644
index 0000000..df9a439
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Strings;
+
+public class ServerName
+{
+ protected short nameType;
+ protected Object name;
+
+ public ServerName(short nameType, Object name)
+ {
+ if (!isCorrectType(nameType, name))
+ {
+ throw new IllegalArgumentException("'name' is not an instance of the correct type");
+ }
+
+ this.nameType = nameType;
+ this.name = name;
+ }
+
+ public short getNameType()
+ {
+ return nameType;
+ }
+
+ public Object getName()
+ {
+ return name;
+ }
+
+ public String getHostName()
+ {
+ if (!isCorrectType(NameType.host_name, name))
+ {
+ throw new IllegalStateException("'name' is not a HostName string");
+ }
+ return (String)name;
+ }
+
+ /**
+ * Encode this {@link ServerName} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ TlsUtils.writeUint8(nameType, output);
+
+ switch (nameType)
+ {
+ case NameType.host_name:
+ byte[] utf8Encoding = Strings.toUTF8ByteArray((String)name);
+ if (utf8Encoding.length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ TlsUtils.writeOpaque16(utf8Encoding, output);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /**
+ * Parse a {@link ServerName} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link ServerName} object.
+ * @throws IOException
+ */
+ public static ServerName parse(InputStream input) throws IOException
+ {
+ short name_type = TlsUtils.readUint8(input);
+ Object name;
+
+ switch (name_type)
+ {
+ case NameType.host_name:
+ {
+ byte[] utf8Encoding = TlsUtils.readOpaque16(input);
+ if (utf8Encoding.length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ name = Strings.fromUTF8ByteArray(utf8Encoding);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return new ServerName(name_type, name);
+ }
+
+ protected static boolean isCorrectType(short nameType, Object name)
+ {
+ switch (nameType)
+ {
+ case NameType.host_name:
+ return name instanceof String;
+ default:
+ throw new IllegalArgumentException("'name' is an unsupported value");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java
new file mode 100644
index 0000000..1dc81f0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+public class ServerNameList
+{
+ protected Vector serverNameList;
+
+ /**
+ * @param serverNameList a {@link Vector} of {@link ServerName}.
+ */
+ public ServerNameList(Vector serverNameList)
+ {
+ if (serverNameList == null || serverNameList.isEmpty())
+ {
+ throw new IllegalArgumentException("'serverNameList' must not be null or empty");
+ }
+
+ this.serverNameList = serverNameList;
+ }
+
+ /**
+ * @return a {@link Vector} of {@link ServerName}.
+ */
+ public Vector getServerNameList()
+ {
+ return serverNameList;
+ }
+
+ /**
+ * Encode this {@link ServerNameList} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ for (int i = 0; i < serverNameList.size(); ++i)
+ {
+ ServerName entry = (ServerName)serverNameList.elementAt(i);
+ entry.encode(buf);
+ }
+
+ TlsUtils.checkUint16(buf.size());
+ TlsUtils.writeUint16(buf.size(), output);
+ buf.writeTo(output);
+ }
+
+ /**
+ * Parse a {@link ServerNameList} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link ServerNameList} object.
+ * @throws IOException
+ */
+ public static ServerNameList parse(InputStream input) throws IOException
+ {
+ int length = TlsUtils.readUint16(input);
+ if (length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ byte[] data = TlsUtils.readFully(length, input);
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(data);
+
+ Vector server_name_list = new Vector();
+ while (buf.available() > 0)
+ {
+ ServerName entry = ServerName.parse(buf);
+ server_name_list.addElement(entry);
+ }
+
+ return new ServerNameList(server_name_list);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
new file mode 100644
index 0000000..68412f8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
@@ -0,0 +1,142 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.util.Arrays;
+
+public final class SessionParameters
+{
+ public static final class Builder
+ {
+ private int cipherSuite = -1;
+ private short compressionAlgorithm = -1;
+ private byte[] masterSecret = null;
+ private Certificate peerCertificate = null;
+ private byte[] encodedServerExtensions = null;
+
+ public Builder()
+ {
+ }
+
+ public SessionParameters build()
+ {
+ validate(this.cipherSuite >= 0, "cipherSuite");
+ validate(this.compressionAlgorithm >= 0, "compressionAlgorithm");
+ validate(this.masterSecret != null, "masterSecret");
+ return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate,
+ encodedServerExtensions);
+ }
+
+ public Builder setCipherSuite(int cipherSuite)
+ {
+ this.cipherSuite = cipherSuite;
+ return this;
+ }
+
+ public Builder setCompressionAlgorithm(short compressionAlgorithm)
+ {
+ this.compressionAlgorithm = compressionAlgorithm;
+ return this;
+ }
+
+ public Builder setMasterSecret(byte[] masterSecret)
+ {
+ this.masterSecret = masterSecret;
+ return this;
+ }
+
+ public Builder setPeerCertificate(Certificate peerCertificate)
+ {
+ this.peerCertificate = peerCertificate;
+ return this;
+ }
+
+ public Builder setServerExtensions(Hashtable serverExtensions)
+ throws IOException
+ {
+ if (serverExtensions == null)
+ {
+ encodedServerExtensions = null;
+ }
+ else
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ TlsProtocol.writeExtensions(buf, serverExtensions);
+ encodedServerExtensions = buf.toByteArray();
+ }
+ return this;
+ }
+
+ private void validate(boolean condition, String parameter)
+ {
+ if (!condition)
+ {
+ throw new IllegalStateException("Required session parameter '" + parameter + "' not configured");
+ }
+ }
+ }
+
+ private int cipherSuite;
+ private short compressionAlgorithm;
+ private byte[] masterSecret;
+ private Certificate peerCertificate;
+ private byte[] encodedServerExtensions;
+
+ private SessionParameters(int cipherSuite, short compressionAlgorithm, byte[] masterSecret,
+ Certificate peerCertificate, byte[] encodedServerExtensions)
+ {
+ this.cipherSuite = cipherSuite;
+ this.compressionAlgorithm = compressionAlgorithm;
+ this.masterSecret = Arrays.clone(masterSecret);
+ this.peerCertificate = peerCertificate;
+ this.encodedServerExtensions = encodedServerExtensions;
+ }
+
+ public void clear()
+ {
+ if (this.masterSecret != null)
+ {
+ Arrays.fill(this.masterSecret, (byte)0);
+ }
+ }
+
+ public SessionParameters copy()
+ {
+ return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate,
+ encodedServerExtensions);
+ }
+
+ public int getCipherSuite()
+ {
+ return cipherSuite;
+ }
+
+ public short getCompressionAlgorithm()
+ {
+ return compressionAlgorithm;
+ }
+
+ public byte[] getMasterSecret()
+ {
+ return masterSecret;
+ }
+
+ public Certificate getPeerCertificate()
+ {
+ return peerCertificate;
+ }
+
+ public Hashtable readServerExtensions() throws IOException
+ {
+ if (encodedServerExtensions == null)
+ {
+ return null;
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(encodedServerExtensions);
+ return TlsProtocol.readExtensions(buf);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
index 7ad4644..a5a591e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
@@ -9,9 +9,8 @@ import java.io.OutputStream;
*/
public class SignatureAndHashAlgorithm
{
-
- private short hash;
- private short signature;
+ protected short hash;
+ protected short signature;
/**
* @param hash {@link HashAlgorithm}
@@ -19,7 +18,6 @@ public class SignatureAndHashAlgorithm
*/
public SignatureAndHashAlgorithm(short hash, short signature)
{
-
if (!TlsUtils.isValidUint8(hash))
{
throw new IllegalArgumentException("'hash' should be a uint8");
@@ -65,7 +63,7 @@ public class SignatureAndHashAlgorithm
public int hashCode()
{
- return (getHash() << 8) | getSignature();
+ return (getHash() << 16) | getSignature();
}
/**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java
new file mode 100644
index 0000000..8293135
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.Signer;
+
+class SignerInputBuffer extends ByteArrayOutputStream
+{
+ void updateSigner(Signer s)
+ {
+ s.update(this.buf, 0, count);
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java
index 5a71f9b..4080aaa 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java
@@ -2,19 +2,18 @@ package org.bouncycastle.crypto.tls;
public class SupplementalDataEntry
{
+ protected int dataType;
+ protected byte[] data;
- private int supp_data_type;
- private byte[] data;
-
- public SupplementalDataEntry(int supp_data_type, byte[] data)
+ public SupplementalDataEntry(int dataType, byte[] data)
{
- this.supp_data_type = supp_data_type;
+ this.dataType = dataType;
this.data = data;
}
public int getDataType()
{
- return supp_data_type;
+ return dataType;
}
public byte[] getData()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
index dbf9d79..bb9306a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
@@ -10,7 +10,6 @@ import org.bouncycastle.util.Arrays;
public class TlsAEADCipher
implements TlsCipher
{
-
protected TlsContext context;
protected int macSize;
protected int nonce_explicit_length;
@@ -21,11 +20,9 @@ public class TlsAEADCipher
protected byte[] encryptImplicitNonce, decryptImplicitNonce;
public TlsAEADCipher(TlsContext context, AEADBlockCipher clientWriteCipher, AEADBlockCipher serverWriteCipher,
- int cipherKeySize, int macSize)
- throws IOException
+ int cipherKeySize, int macSize) throws IOException
{
-
- if (!ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion()))
+ if (!TlsUtils.isTLSv12(context))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
@@ -33,7 +30,7 @@ public class TlsAEADCipher
this.context = context;
this.macSize = macSize;
- // NOTE: Valid for RFC 5288 ciphers but may need review for other AEAD ciphers
+ // NOTE: Valid for RFC 5288/6655 ciphers but may need review for other AEAD ciphers
this.nonce_explicit_length = 8;
// TODO SecurityParameters.fixed_iv_length
@@ -94,12 +91,11 @@ public class TlsAEADCipher
public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
throws IOException
{
-
byte[] nonce = new byte[this.encryptImplicitNonce.length + nonce_explicit_length];
System.arraycopy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.length);
/*
- * RFC 5288 The nonce_explicit MAY be the 64-bit sequence number.
+ * RFC 5288/6655 The nonce_explicit MAY be the 64-bit sequence number.
*
* (May need review for other AEAD ciphers).
*/
@@ -113,12 +109,13 @@ public class TlsAEADCipher
System.arraycopy(nonce, encryptImplicitNonce.length, output, 0, nonce_explicit_length);
int outputPos = nonce_explicit_length;
- encryptCipher.init(true,
- new AEADParameters(null, 8 * macSize, nonce, getAdditionalData(seqNo, type, plaintextLength)));
+ byte[] additionalData = getAdditionalData(seqNo, type, plaintextLength);
+ AEADParameters parameters = new AEADParameters(null, 8 * macSize, nonce, additionalData);
- outputPos += encryptCipher.processBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos);
try
{
+ encryptCipher.init(true, parameters);
+ outputPos += encryptCipher.processBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos);
outputPos += encryptCipher.doFinal(output, outputPos);
}
catch (Exception e)
@@ -138,7 +135,6 @@ public class TlsAEADCipher
public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
throws IOException
{
-
if (getPlaintextLimit(len) < 0)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
@@ -155,13 +151,13 @@ public class TlsAEADCipher
byte[] output = new byte[plaintextLength];
int outputPos = 0;
- decryptCipher.init(false,
- new AEADParameters(null, 8 * macSize, nonce, getAdditionalData(seqNo, type, plaintextLength)));
-
- outputPos += decryptCipher.processBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos);
+ byte[] additionalData = getAdditionalData(seqNo, type, plaintextLength);
+ AEADParameters parameters = new AEADParameters(null, 8 * macSize, nonce, additionalData);
try
{
+ decryptCipher.init(false, parameters);
+ outputPos += decryptCipher.processBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos);
outputPos += decryptCipher.doFinal(output, outputPos);
}
catch (Exception e)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
index 0b218c1..2f9e8a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
@@ -16,6 +16,7 @@ import org.bouncycastle.util.Arrays;
public class TlsBlockCipher
implements TlsCipher
{
+ private static boolean encryptThenMAC = false;
protected TlsContext context;
protected byte[] randomData;
@@ -38,17 +39,14 @@ public class TlsBlockCipher
}
public TlsBlockCipher(TlsContext context, BlockCipher clientWriteCipher, BlockCipher serverWriteCipher,
- Digest clientWriteDigest, Digest serverWriteDigest, int cipherKeySize)
- throws IOException
+ Digest clientWriteDigest, Digest serverWriteDigest, int cipherKeySize) throws IOException
{
-
this.context = context;
this.randomData = new byte[256];
context.getSecureRandom().nextBytes(randomData);
- this.useExplicitIV = ProtocolVersion.TLSv11.isEqualOrEarlierVersionOf(context.getServerVersion()
- .getEquivalentTLSVersion());
+ this.useExplicitIV = TlsUtils.isTLSv11(context);
int key_block_size = (2 * cipherKeySize) + clientWriteDigest.getDigestSize()
+ serverWriteDigest.getDigestSize();
@@ -123,13 +121,30 @@ public class TlsBlockCipher
int blockSize = encryptCipher.getBlockSize();
int macSize = writeMac.getSize();
- int result = ciphertextLimit - (ciphertextLimit % blockSize) - macSize - 1;
+ int plaintextLimit = ciphertextLimit;
+
+ // An explicit IV consumes 1 block
if (useExplicitIV)
{
- result -= blockSize;
+ plaintextLimit -= blockSize;
}
- return result;
+ // Leave room for the MAC, and require block-alignment
+ if (encryptThenMAC)
+ {
+ plaintextLimit -= macSize;
+ plaintextLimit -= plaintextLimit % blockSize;
+ }
+ else
+ {
+ plaintextLimit -= plaintextLimit % blockSize;
+ plaintextLimit -= macSize;
+ }
+
+ // Minimum 1 byte of padding
+ --plaintextLimit;
+
+ return plaintextLimit;
}
public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
@@ -139,7 +154,13 @@ public class TlsBlockCipher
ProtocolVersion version = context.getServerVersion();
- int padding_length = blockSize - 1 - ((len + macSize) % blockSize);
+ int enc_input_length = len;
+ if (!encryptThenMAC)
+ {
+ enc_input_length += macSize;
+ }
+
+ int padding_length = blockSize - 1 - (enc_input_length % blockSize);
// TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
if (!version.isDTLS() && !version.isSSL())
@@ -156,7 +177,7 @@ public class TlsBlockCipher
totalSize += blockSize;
}
- byte[] outbuf = new byte[totalSize];
+ byte[] outBuf = new byte[totalSize];
int outOff = 0;
if (useExplicitIV)
@@ -166,25 +187,42 @@ public class TlsBlockCipher
encryptCipher.init(true, new ParametersWithIV(null, explicitIV));
- System.arraycopy(explicitIV, 0, outbuf, outOff, blockSize);
+ System.arraycopy(explicitIV, 0, outBuf, outOff, blockSize);
outOff += blockSize;
}
- byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+ int blocks_start = outOff;
+
+ System.arraycopy(plaintext, offset, outBuf, outOff, len);
+ outOff += len;
- System.arraycopy(plaintext, offset, outbuf, outOff, len);
- System.arraycopy(mac, 0, outbuf, outOff + len, mac.length);
+ if (!encryptThenMAC)
+ {
+ byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+ System.arraycopy(mac, 0, outBuf, outOff, mac.length);
+ outOff += mac.length;
+ }
- int padOffset = outOff + len + mac.length;
for (int i = 0; i <= padding_length; i++)
{
- outbuf[i + padOffset] = (byte)padding_length;
+ outBuf[outOff++] = (byte)padding_length;
}
- for (int i = outOff; i < totalSize; i += blockSize)
+
+ for (int i = blocks_start; i < outOff; i += blockSize)
{
- encryptCipher.processBlock(outbuf, i, outbuf, i);
+ encryptCipher.processBlock(outBuf, i, outBuf, i);
}
- return outbuf;
+
+ if (encryptThenMAC)
+ {
+ byte[] mac = writeMac.calculateMac(seqNo, type, outBuf, 0, outOff);
+ System.arraycopy(mac, 0, outBuf, outOff, mac.length);
+ outOff += mac.length;
+ }
+
+// assert outBuf.length == outOff;
+
+ return outBuf;
}
public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
@@ -193,7 +231,16 @@ public class TlsBlockCipher
int blockSize = decryptCipher.getBlockSize();
int macSize = readMac.getSize();
- int minLen = Math.max(blockSize, macSize + 1);
+ int minLen = blockSize;
+ if (encryptThenMAC)
+ {
+ minLen += macSize;
+ }
+ else
+ {
+ minLen = Math.max(minLen, macSize + 1);
+ }
+
if (useExplicitIV)
{
minLen += blockSize;
@@ -204,41 +251,67 @@ public class TlsBlockCipher
throw new TlsFatalAlert(AlertDescription.decode_error);
}
- if (len % blockSize != 0)
+ int blocks_length = len;
+ if (encryptThenMAC)
+ {
+ blocks_length -= macSize;
+ }
+
+ if (blocks_length % blockSize != 0)
{
throw new TlsFatalAlert(AlertDescription.decryption_failed);
}
+ if (encryptThenMAC)
+ {
+ int end = offset + len;
+ byte[] receivedMac = Arrays.copyOfRange(ciphertext, end - macSize, end);
+ byte[] calculatedMac = readMac.calculateMac(seqNo, type, ciphertext, offset, len - macSize);
+
+ boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
+
+ if (badMac)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+ }
+
if (useExplicitIV)
{
decryptCipher.init(false, new ParametersWithIV(null, ciphertext, offset, blockSize));
offset += blockSize;
- len -= blockSize;
+ blocks_length -= blockSize;
}
- for (int i = 0; i < len; i += blockSize)
+ for (int i = 0; i < blocks_length; i += blockSize)
{
decryptCipher.processBlock(ciphertext, offset + i, ciphertext, offset + i);
}
// If there's anything wrong with the padding, this will return zero
- int totalPad = checkPaddingConstantTime(ciphertext, offset, len, blockSize, macSize);
+ int totalPad = checkPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMAC ? 0 : macSize);
- int macInputLen = len - totalPad - macSize;
+ int dec_output_length = blocks_length - totalPad;
- byte[] decryptedMac = Arrays.copyOfRange(ciphertext, offset + macInputLen, offset + macInputLen + macSize);
- byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen, len
- - macSize, randomData);
+ if (!encryptThenMAC)
+ {
+ dec_output_length -= macSize;
+ int macInputLen = dec_output_length;
+ int macOff = offset + macInputLen;
+ byte[] receivedMac = Arrays.copyOfRange(ciphertext, macOff, macOff + macSize);
+ byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen,
+ blocks_length - macSize, randomData);
- boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, decryptedMac);
+ boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
- if (badMac || totalPad == 0)
- {
- throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ if (badMac || totalPad == 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
}
- return Arrays.copyOfRange(ciphertext, offset, offset + macInputLen);
+ return Arrays.copyOfRange(ciphertext, offset, offset + dec_output_length);
}
protected int checkPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
@@ -251,7 +324,7 @@ public class TlsBlockCipher
int dummyIndex = 0;
byte padDiff = 0;
- if ((context.getServerVersion().isSSL() && totalPad > blockSize) || (macSize + totalPad > len))
+ if ((TlsUtils.isSSL(context) && totalPad > blockSize) || (macSize + totalPad > len))
{
totalPad = 0;
}
@@ -310,4 +383,4 @@ public class TlsBlockCipher
}
return n;
}
-} \ No newline at end of file
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
index 62444fa..7db86cd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
@@ -7,9 +7,18 @@ import java.util.Vector;
public interface TlsClient
extends TlsPeer
{
-
void init(TlsClientContext context);
+ /**
+ * Return the session this client wants to resume, if any. Note that the peer's certificate
+ * chain for the session (if any) may need to be periodically revalidated.
+ *
+ * @return A {@link TlsSession} representing the resumable session to be used for this
+ * connection, or null to use a new session.
+ * @see SessionParameters#getPeerCertificate()
+ */
+ TlsSession getSessionToResume();
+
ProtocolVersion getClientHelloRecordLayerVersion();
ProtocolVersion getClientVersion();
@@ -25,15 +34,18 @@ public interface TlsClient
void notifyServerVersion(ProtocolVersion selectedVersion)
throws IOException;
+ /**
+ * Notifies the client of the session_id sent in the ServerHello.
+ *
+ * @param sessionID
+ * @see {@link TlsContext#getResumableSession()}
+ */
void notifySessionID(byte[] sessionID);
void notifySelectedCipherSuite(int selectedCipherSuite);
void notifySelectedCompressionMethod(short selectedCompressionMethod);
- void notifySecureRenegotiation(boolean secureNegotiation)
- throws IOException;
-
// Hashtable is (Integer -> byte[])
void processServerExtensions(Hashtable serverExtensions)
throws IOException;
@@ -52,12 +64,6 @@ public interface TlsClient
Vector getClientSupplementalData()
throws IOException;
- TlsCompression getCompression()
- throws IOException;
-
- TlsCipher getCipher()
- throws IOException;
-
/**
* RFC 5077 3.3. NewSessionTicket Handshake Message
* <p/>
@@ -70,7 +76,4 @@ public interface TlsClient
*/
void notifyNewSessionTicket(NewSessionTicket newSessionTicket)
throws IOException;
-
- void notifyHandshakeComplete()
- throws IOException;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java
index d91f7f8..b320144 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java
@@ -6,7 +6,6 @@ class TlsClientContextImpl
extends AbstractTlsContext
implements TlsClientContext
{
-
TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
{
super(secureRandom, securityParameters);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
index 33cd914..506560f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
@@ -1,7 +1,6 @@
package org.bouncycastle.crypto.tls;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -16,19 +15,15 @@ import org.bouncycastle.util.Arrays;
public class TlsClientProtocol
extends TlsProtocol
{
-
protected TlsClient tlsClient = null;
protected TlsClientContextImpl tlsClientContext = null;
- protected int[] offeredCipherSuites = null;
- protected short[] offeredCompressionMethods = null;
- protected Hashtable clientExtensions = null;
-
- protected int selectedCipherSuite;
- protected short selectedCompressionMethod;
+ protected byte[] selectedSessionID = null;
protected TlsKeyExchange keyExchange = null;
protected TlsAuthentication authentication = null;
+
+ protected CertificateStatus certificateStatus = null;
protected CertificateRequest certificateRequest = null;
private static SecureRandom createSecureRandom()
@@ -61,11 +56,10 @@ public class TlsClientProtocol
/**
* Initiates a TLS handshake in the role of client
*
- * @param tlsClient
+ * @param tlsClient The {@link TlsClient} to use for the handshake.
* @throws IOException If handshake was not successful.
*/
- public void connect(TlsClient tlsClient)
- throws IOException
+ public void connect(TlsClient tlsClient) throws IOException
{
if (tlsClient == null)
{
@@ -73,7 +67,7 @@ public class TlsClientProtocol
}
if (this.tlsClient != null)
{
- throw new IllegalStateException("connect can only be called once");
+ throw new IllegalStateException("'connect' can only be called once");
}
this.tlsClient = tlsClient;
@@ -86,12 +80,32 @@ public class TlsClientProtocol
this.tlsClient.init(tlsClientContext);
this.recordStream.init(tlsClientContext);
+ TlsSession sessionToResume = tlsClient.getSessionToResume();
+ if (sessionToResume != null)
+ {
+ SessionParameters sessionParameters = sessionToResume.exportSessionParameters();
+ if (sessionParameters != null)
+ {
+ this.tlsSession = sessionToResume;
+ this.sessionParameters = sessionParameters;
+ }
+ }
+
sendClientHelloMessage();
this.connection_state = CS_CLIENT_HELLO;
completeHandshake();
+ }
- this.tlsClient.notifyHandshakeComplete();
+ protected void cleanupHandshake()
+ {
+ super.cleanupHandshake();
+
+ this.selectedSessionID = null;
+ this.keyExchange = null;
+ this.authentication = null;
+ this.certificateStatus = null;
+ this.certificateRequest = null;
}
protected AbstractTlsContext getContext()
@@ -104,36 +118,27 @@ public class TlsClientProtocol
return tlsClient;
}
- protected void handleChangeCipherSpecMessage()
+ protected void handleHandshakeMessage(short type, byte[] data)
throws IOException
{
+ ByteArrayInputStream buf = new ByteArrayInputStream(data);
- switch (this.connection_state)
+ if (this.resumedSession)
{
- case CS_CLIENT_FINISHED:
- {
- if (this.expectSessionTicket)
+ if (type != HandshakeType.finished || this.connection_state != CS_SERVER_HELLO)
{
- /*
- * RFC 5077 3.3. This message MUST be sent if the server included a SessionTicket
- * extension in the ServerHello.
- */
- this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- // NB: Fall through to next case label
- }
- case CS_SERVER_SESSION_TICKET:
- this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC;
- break;
- default:
- this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- }
- }
- protected void handleHandshakeMessage(short type, byte[] data)
- throws IOException
- {
- ByteArrayInputStream buf = new ByteArrayInputStream(data);
+ processFinishedMessage(buf);
+ this.connection_state = CS_SERVER_FINISHED;
+
+ sendFinishedMessage();
+ this.connection_state = CS_CLIENT_FINISHED;
+ this.connection_state = CS_END;
+
+ return;
+ }
switch (type)
{
@@ -150,72 +155,143 @@ public class TlsClientProtocol
{
// Parse the Certificate message and send to cipher suite
- Certificate serverCertificate = Certificate.parse(buf);
+ this.peerCertificate = Certificate.parse(buf);
assertEmpty(buf);
- this.keyExchange.processServerCertificate(serverCertificate);
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (this.peerCertificate == null || this.peerCertificate.isEmpty())
+ {
+ this.allowCertificateStatus = false;
+ }
+
+ this.keyExchange.processServerCertificate(this.peerCertificate);
this.authentication = tlsClient.getAuthentication();
- this.authentication.notifyServerCertificate(serverCertificate);
+ this.authentication.notifyServerCertificate(this.peerCertificate);
break;
}
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
this.connection_state = CS_SERVER_CERTIFICATE;
break;
}
+ case HandshakeType.certificate_status:
+ {
+ switch (this.connection_state)
+ {
+ case CS_SERVER_CERTIFICATE:
+ {
+ if (!this.allowCertificateStatus)
+ {
+ /*
+ * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the
+ * server MUST have included an extension of type "status_request" with empty
+ * "extension_data" in the extended server hello..
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.certificateStatus = CertificateStatus.parse(buf);
+
+ assertEmpty(buf);
+
+ // TODO[RFC 3546] Figure out how to provide this to the client/authentication.
+
+ this.connection_state = CS_CERTIFICATE_STATUS;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
case HandshakeType.finished:
+ {
switch (this.connection_state)
{
- case CS_SERVER_CHANGE_CIPHER_SPEC:
+ case CS_CLIENT_FINISHED:
+ {
processFinishedMessage(buf);
this.connection_state = CS_SERVER_FINISHED;
+ this.connection_state = CS_END;
break;
+ }
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
+ }
case HandshakeType.server_hello:
+ {
switch (this.connection_state)
{
case CS_CLIENT_HELLO:
+ {
receiveServerHelloMessage(buf);
this.connection_state = CS_SERVER_HELLO;
- securityParameters.prfAlgorithm = getPRFAlgorithm(selectedCipherSuite);
- securityParameters.compressionAlgorithm = this.selectedCompressionMethod;
+ if (this.securityParameters.maxFragmentLength >= 0)
+ {
+ int plainTextLimit = 1 << (8 + this.securityParameters.maxFragmentLength);
+ recordStream.setPlaintextLimit(plainTextLimit);
+ }
+
+ this.securityParameters.prfAlgorithm = getPRFAlgorithm(getContext(),
+ this.securityParameters.getCipherSuite());
/*
* RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
* verify_data_length has a verify_data_length equal to 12. This includes all
* existing cipher suites.
*/
- securityParameters.verifyDataLength = 12;
+ this.securityParameters.verifyDataLength = 12;
- recordStream.notifyHelloComplete();
+ this.recordStream.notifyHelloComplete();
+
+ if (this.resumedSession)
+ {
+ this.securityParameters.masterSecret = Arrays.clone(this.sessionParameters.getMasterSecret());
+ this.recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
+
+ sendChangeCipherSpecMessage();
+ }
+ else
+ {
+ invalidateSession();
+
+ if (this.selectedSessionID.length > 0)
+ {
+ this.tlsSession = new TlsSessionImpl(this.selectedSessionID, null);
+ }
+ }
break;
+ }
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
+ }
case HandshakeType.supplemental_data:
{
switch (this.connection_state)
{
case CS_SERVER_HELLO:
+ {
handleSupplementalData(readSupplementalDataMessage(buf));
break;
+ }
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
case HandshakeType.server_hello_done:
+ {
switch (this.connection_state)
{
case CS_SERVER_HELLO:
@@ -225,7 +301,6 @@ public class TlsClientProtocol
}
case CS_SERVER_SUPPLEMENTAL_DATA:
{
-
// There was no server certificate message; check it's OK
this.keyExchange.skipServerCredentials();
this.authentication = null;
@@ -233,19 +308,22 @@ public class TlsClientProtocol
// NB: Fall through to next case label
}
case CS_SERVER_CERTIFICATE:
-
+ case CS_CERTIFICATE_STATUS:
+ {
// There was no server key exchange message; check it's OK
this.keyExchange.skipServerKeyExchange();
// NB: Fall through to next case label
-
+ }
case CS_SERVER_KEY_EXCHANGE:
case CS_CERTIFICATE_REQUEST:
-
+ {
assertEmpty(buf);
this.connection_state = CS_SERVER_HELLO_DONE;
+ this.recordStream.getHandshakeHash().sealHashAlgorithms();
+
Vector clientSupplementalData = tlsClient.getClientSupplementalData();
if (clientSupplementalData != null)
{
@@ -289,40 +367,56 @@ public class TlsClientProtocol
* in our CipherSuite.
*/
sendClientKeyExchangeMessage();
+ this.connection_state = CS_CLIENT_KEY_EXCHANGE;
establishMasterSecret(getContext(), keyExchange);
+ recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
- /*
- * Initialize our cipher suite
- */
- recordStream.setPendingConnectionState(tlsClient.getCompression(), tlsClient.getCipher());
-
- this.connection_state = CS_CLIENT_KEY_EXCHANGE;
+ TlsHandshakeHash prepareFinishHash = recordStream.prepareToFinish();
if (clientCreds != null && clientCreds instanceof TlsSignerCredentials)
{
+ TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds;
+
/*
- * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm
- * prepended from TLS 1.2
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
*/
- TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds;
- byte[] md5andsha1 = recordStream.getCurrentHash(null);
- byte[] clientCertificateSignature = signerCreds.generateCertificateSignature(md5andsha1);
- sendCertificateVerifyMessage(clientCertificateSignature);
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+ byte[] hash;
+
+ if (TlsUtils.isTLSv12(getContext()))
+ {
+ signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm();
+ if (signatureAndHashAlgorithm == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash());
+ }
+ else
+ {
+ signatureAndHashAlgorithm = null;
+ hash = getCurrentPRFHash(getContext(), prepareFinishHash, null);
+ }
+
+ byte[] signature = signerCredentials.generateCertificateSignature(hash);
+ DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ sendCertificateVerifyMessage(certificateVerify);
this.connection_state = CS_CERTIFICATE_VERIFY;
}
sendChangeCipherSpecMessage();
- this.connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC;
-
sendFinishedMessage();
this.connection_state = CS_CLIENT_FINISHED;
break;
+ }
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
break;
+ }
case HandshakeType.server_key_exchange:
{
switch (this.connection_state)
@@ -334,7 +428,6 @@ public class TlsClientProtocol
}
case CS_SERVER_SUPPLEMENTAL_DATA:
{
-
// There was no server certificate message; check it's OK
this.keyExchange.skipServerCredentials();
this.authentication = null;
@@ -342,14 +435,15 @@ public class TlsClientProtocol
// NB: Fall through to next case label
}
case CS_SERVER_CERTIFICATE:
-
+ case CS_CERTIFICATE_STATUS:
+ {
this.keyExchange.processServerKeyExchange(buf);
assertEmpty(buf);
break;
-
+ }
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
this.connection_state = CS_SERVER_KEY_EXCHANGE;
@@ -360,12 +454,13 @@ public class TlsClientProtocol
switch (this.connection_state)
{
case CS_SERVER_CERTIFICATE:
-
+ case CS_CERTIFICATE_STATUS:
+ {
// There was no server key exchange message; check it's OK
this.keyExchange.skipServerKeyExchange();
// NB: Fall through to next case label
-
+ }
case CS_SERVER_KEY_EXCHANGE:
{
if (this.authentication == null)
@@ -374,19 +469,26 @@ public class TlsClientProtocol
* RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server
* to request client identification.
*/
- this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
- this.certificateRequest = CertificateRequest.parse(buf);
+ this.certificateRequest = CertificateRequest.parse(getContext(), buf);
assertEmpty(buf);
this.keyExchange.validateCertificateRequest(this.certificateRequest);
+ /*
+ * TODO Give the client a chance to immediately select the CertificateVerify hash
+ * algorithm here to avoid tracking the other hash algorithms unnecessarily?
+ */
+ TlsUtils.trackHashAlgorithms(this.recordStream.getHandshakeHash(),
+ this.certificateRequest.getSupportedSignatureAlgorithms());
+
break;
}
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
this.connection_state = CS_CERTIFICATE_REQUEST;
@@ -397,23 +499,32 @@ public class TlsClientProtocol
switch (this.connection_state)
{
case CS_CLIENT_FINISHED:
+ {
if (!this.expectSessionTicket)
{
/*
* RFC 5077 3.3. This message MUST NOT be sent if the server did not include a
* SessionTicket extension in the ServerHello.
*/
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
+
+ /*
+ * RFC 5077 3.4. If the client receives a session ticket from the server, then it
+ * discards any Session ID that was sent in the ServerHello.
+ */
+ invalidateSession();
+
receiveNewSessionTicketMessage(buf);
this.connection_state = CS_SERVER_SESSION_TICKET;
break;
+ }
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
case HandshakeType.hello_request:
-
+ {
assertEmpty(buf);
/*
@@ -422,27 +533,34 @@ public class TlsClientProtocol
* if it does not wish to renegotiate a session, or the client may, if it wishes,
* respond with a no_renegotiation alert.
*/
- if (this.connection_state == CS_SERVER_FINISHED)
+ if (this.connection_state == CS_END)
{
+ /*
+ * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal
+ * handshake_failure alert.
+ */
+ if (TlsUtils.isSSL(getContext()))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
String message = "Renegotiation not supported";
raiseWarning(AlertDescription.no_renegotiation, message);
}
break;
+ }
+ case HandshakeType.client_hello:
case HandshakeType.client_key_exchange:
case HandshakeType.certificate_verify:
- case HandshakeType.client_hello:
case HandshakeType.hello_verify_request:
default:
- // We do not support this!
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
protected void handleSupplementalData(Vector serverSupplementalData)
throws IOException
{
-
this.tlsClient.processServerSupplementalData(serverSupplementalData);
this.connection_state = CS_SERVER_SUPPLEMENTAL_DATA;
@@ -453,7 +571,6 @@ public class TlsClientProtocol
protected void receiveNewSessionTicketMessage(ByteArrayInputStream buf)
throws IOException
{
-
NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf);
TlsProtocol.assertEmpty(buf);
@@ -464,23 +581,22 @@ public class TlsClientProtocol
protected void receiveServerHelloMessage(ByteArrayInputStream buf)
throws IOException
{
-
ProtocolVersion server_version = TlsUtils.readVersion(buf);
if (server_version.isDTLS())
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
// Check that this matches what the server is sending in the record layer
- if (!server_version.equals(recordStream.getReadVersion()))
+ if (!server_version.equals(this.recordStream.getReadVersion()))
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
ProtocolVersion client_version = getContext().getClientVersion();
if (!server_version.isEqualOrEarlierVersionOf(client_version))
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
this.recordStream.setWriteVersion(server_version);
@@ -490,38 +606,41 @@ public class TlsClientProtocol
/*
* Read the server random
*/
- securityParameters.serverRandom = TlsUtils.readFully(32, buf);
+ this.securityParameters.serverRandom = TlsUtils.readFully(32, buf);
- byte[] sessionID = TlsUtils.readOpaque8(buf);
- if (sessionID.length > 32)
+ this.selectedSessionID = TlsUtils.readOpaque8(buf);
+ if (this.selectedSessionID.length > 32)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- this.tlsClient.notifySessionID(sessionID);
+ this.tlsClient.notifySessionID(this.selectedSessionID);
+
+ this.resumedSession = this.selectedSessionID.length > 0 && this.tlsSession != null
+ && Arrays.areEqual(this.selectedSessionID, this.tlsSession.getSessionID());
/*
* Find out which CipherSuite the server has chosen and check that it was one of the offered
* ones.
*/
- this.selectedCipherSuite = TlsUtils.readUint16(buf);
- if (!arrayContains(offeredCipherSuites, this.selectedCipherSuite)
- || this.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
- || this.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ int selectedCipherSuite = TlsUtils.readUint16(buf);
+ if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite)
+ || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- this.tlsClient.notifySelectedCipherSuite(this.selectedCipherSuite);
+ this.tlsClient.notifySelectedCipherSuite(selectedCipherSuite);
/*
* Find out which CompressionMethod the server has chosen and check that it was one of the
* offered ones.
*/
short selectedCompressionMethod = TlsUtils.readUint8(buf);
- if (!arrayContains(offeredCompressionMethods, selectedCompressionMethod))
+ if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod))
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod);
@@ -534,15 +653,7 @@ public class TlsClientProtocol
* possibility that the extended server hello message could "break" existing TLS 1.0
* clients.
*/
-
- /*
- * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
- * extensions appearing in the client hello, and send a server hello containing no
- * extensions.
- */
-
- // Integer -> byte[]
- Hashtable serverExtensions = readExtensions(buf);
+ this.serverExtensions = readExtensions(buf);
/*
* RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
@@ -551,9 +662,9 @@ public class TlsClientProtocol
* However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
* Hello is always allowed.
*/
- if (serverExtensions != null)
+ if (this.serverExtensions != null)
{
- Enumeration e = serverExtensions.keys();
+ Enumeration e = this.serverExtensions.keys();
while (e.hasMoreElements())
{
Integer extType = (Integer)e.nextElement();
@@ -565,107 +676,169 @@ public class TlsClientProtocol
* only allowed because the client is signaling its willingness to receive the
* extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
*/
- if (!extType.equals(EXT_RenegotiationInfo)
- && (clientExtensions == null || clientExtensions.get(extType) == null))
+ if (extType.equals(EXT_RenegotiationInfo))
{
- /*
- * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless
- * the same extension type appeared in the corresponding ClientHello. If a
- * client receives an extension type in ServerHello that it did not request in
- * the associated ClientHello, it MUST abort the handshake with an
- * unsupported_extension fatal alert.
- */
- this.failWithError(AlertLevel.fatal, AlertDescription.unsupported_extension);
+ continue;
+ }
+
+ /*
+ * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and send a server hello containing no
+ * extensions[.]
+ */
+ if (this.resumedSession)
+ {
+ // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats
+ // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats
+ // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats
+// throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ /*
+ * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the
+ * same extension type appeared in the corresponding ClientHello. If a client
+ * receives an extension type in ServerHello that it did not request in the
+ * associated ClientHello, it MUST abort the handshake with an unsupported_extension
+ * fatal alert.
+ */
+ if (null == TlsUtils.getExtensionData(this.clientExtensions, extType))
+ {
+ throw new TlsFatalAlert(AlertDescription.unsupported_extension);
}
}
+ }
+ /*
+ * RFC 5746 3.4. Client Behavior: Initial Handshake
+ */
+ {
/*
- * RFC 5746 3.4. Client Behavior: Initial Handshake
+ * When a ServerHello is received, the client MUST check if it includes the
+ * "renegotiation_info" extension:
*/
+ byte[] renegExtData = TlsUtils.getExtensionData(this.serverExtensions, EXT_RenegotiationInfo);
+ if (renegExtData != null)
{
/*
- * When a ServerHello is received, the client MUST check if it includes the
- * "renegotiation_info" extension:
+ * If the extension is present, set the secure_renegotiation flag to TRUE. The
+ * client MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake (by sending a fatal
+ * handshake_failure alert).
*/
- byte[] renegExtValue = (byte[])serverExtensions.get(EXT_RenegotiationInfo);
- if (renegExtValue != null)
- {
- /*
- * If the extension is present, set the secure_renegotiation flag to TRUE. The
- * client MUST then verify that the length of the "renegotiated_connection"
- * field is zero, and if it is not, MUST abort the handshake (by sending a fatal
- * handshake_failure alert).
- */
- this.secure_renegotiation = true;
+ this.secure_renegotiation = true;
- if (!Arrays.constantTimeAreEqual(renegExtValue, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- }
+ if (!Arrays.constantTimeAreEqual(renegExtData, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
}
+ }
+
+ // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming
+ this.tlsClient.notifySecureRenegotiation(this.secure_renegotiation);
- this.expectSessionTicket = serverExtensions.containsKey(EXT_SessionTicket);
+ Hashtable sessionClientExtensions = clientExtensions, sessionServerExtensions = serverExtensions;
+ if (this.resumedSession)
+ {
+ if (selectedCipherSuite != this.sessionParameters.getCipherSuite()
+ || selectedCompressionMethod != this.sessionParameters.getCompressionAlgorithm())
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ sessionClientExtensions = null;
+ sessionServerExtensions = this.sessionParameters.readServerExtensions();
}
- tlsClient.notifySecureRenegotiation(this.secure_renegotiation);
+ this.securityParameters.cipherSuite = selectedCipherSuite;
+ this.securityParameters.compressionAlgorithm = selectedCompressionMethod;
- if (clientExtensions != null)
+ if (sessionServerExtensions != null)
+ {
+ this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(sessionClientExtensions,
+ sessionServerExtensions, AlertDescription.illegal_parameter);
+
+ this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(sessionServerExtensions);
+
+ /*
+ * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
+ * a session resumption handshake.
+ */
+ this.allowCertificateStatus = !this.resumedSession
+ && TlsUtils.hasExpectedEmptyExtensionData(sessionServerExtensions,
+ TlsExtensionsUtils.EXT_status_request, AlertDescription.illegal_parameter);
+
+ this.expectSessionTicket = !this.resumedSession
+ && TlsUtils.hasExpectedEmptyExtensionData(sessionServerExtensions, TlsProtocol.EXT_SessionTicket,
+ AlertDescription.illegal_parameter);
+ }
+
+ if (sessionClientExtensions != null)
{
- tlsClient.processServerExtensions(serverExtensions);
+ this.tlsClient.processServerExtensions(sessionServerExtensions);
}
}
- protected void sendCertificateVerifyMessage(byte[] data)
+ protected void sendCertificateVerifyMessage(DigitallySigned certificateVerify)
throws IOException
{
- /*
- * Send signature of handshake messages so far to prove we are the owner of the cert See RFC
- * 2246 sections 4.7, 7.4.3 and 7.4.8
- */
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- TlsUtils.writeUint8(HandshakeType.certificate_verify, bos);
- TlsUtils.writeUint24(data.length + 2, bos);
- TlsUtils.writeOpaque16(data, bos);
- byte[] message = bos.toByteArray();
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify);
+
+ certificateVerify.encode(message);
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
}
protected void sendClientHelloMessage()
throws IOException
{
-
- recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion());
-
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeUint8(HandshakeType.client_hello, buf);
-
- // Reserve space for length
- TlsUtils.writeUint24(0, buf);
+ this.recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion());
ProtocolVersion client_version = this.tlsClient.getClientVersion();
if (client_version.isDTLS())
{
- this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
getContext().setClientVersion(client_version);
- TlsUtils.writeVersion(client_version, buf);
-
- buf.write(securityParameters.clientRandom);
-
- // Session id
- TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
/*
- * Cipher suites
+ * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a
+ * Session ID in the TLS ClientHello.
*/
+ byte[] session_id = TlsUtils.EMPTY_BYTES;
+ if (this.tlsSession != null)
+ {
+ session_id = this.tlsSession.getSessionID();
+ if (session_id == null || session_id.length > 32)
+ {
+ session_id = TlsUtils.EMPTY_BYTES;
+ }
+ }
+
this.offeredCipherSuites = this.tlsClient.getCipherSuites();
- // Integer -> byte[]
+ this.offeredCompressionMethods = this.tlsClient.getCompressionMethods();
+
+ if (session_id.length > 0 && this.sessionParameters != null)
+ {
+ if (!Arrays.contains(this.offeredCipherSuites, sessionParameters.getCipherSuite())
+ || !Arrays.contains(this.offeredCompressionMethods, sessionParameters.getCompressionAlgorithm()))
+ {
+ session_id = TlsUtils.EMPTY_BYTES;
+ }
+ }
+
this.clientExtensions = this.tlsClient.getClientExtensions();
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
+
+ TlsUtils.writeVersion(client_version, message);
+
+ message.write(this.securityParameters.getClientRandom());
+
+ TlsUtils.writeOpaque8(session_id, message);
+
// Cipher Suites (and SCSV)
{
/*
@@ -673,60 +846,39 @@ public class TlsClientProtocol
* or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
* ClientHello. Including both is NOT RECOMMENDED.
*/
- boolean noRenegExt = clientExtensions == null || clientExtensions.get(EXT_RenegotiationInfo) == null;
+ byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo);
+ boolean noRenegExt = (null == renegExtData);
- int count = offeredCipherSuites.length;
- if (noRenegExt)
- {
- // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
- ++count;
- }
+ boolean noSCSV = !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
- TlsUtils.writeUint16(2 * count, buf);
- TlsUtils.writeUint16Array(offeredCipherSuites, buf);
-
- if (noRenegExt)
+ if (noRenegExt && noSCSV)
{
- TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, buf);
+ // TODO Consider whether to default to a client extension instead
+// this.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.clientExtensions);
+// this.clientExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
+ this.offeredCipherSuites = Arrays.append(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
}
- }
- // Compression methods
- this.offeredCompressionMethods = this.tlsClient.getCompressionMethods();
+ TlsUtils.writeUint16ArrayWithUint16Length(offeredCipherSuites, message);
+ }
- TlsUtils.writeUint8((short)offeredCompressionMethods.length, buf);
- TlsUtils.writeUint8Array(offeredCompressionMethods, buf);
+ TlsUtils.writeUint8ArrayWithUint8Length(offeredCompressionMethods, message);
- // Extensions
if (clientExtensions != null)
{
- writeExtensions(buf, clientExtensions);
+ writeExtensions(message, clientExtensions);
}
- byte[] message = buf.toByteArray();
-
- // Patch actual length back in
- TlsUtils.writeUint24(message.length - 4, message, 1);
-
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
}
protected void sendClientKeyExchangeMessage()
throws IOException
{
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- TlsUtils.writeUint8(HandshakeType.client_key_exchange, bos);
-
- // Reserve space for length
- TlsUtils.writeUint24(0, bos);
-
- this.keyExchange.generateClientKeyExchange(bos);
- byte[] message = bos.toByteArray();
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange);
- // Patch actual length back in
- TlsUtils.writeUint24(message.length - 4, message, 1);
+ this.keyExchange.generateClientKeyExchange(message);
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
index dfb1052..04781ef 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
@@ -4,7 +4,6 @@ import java.security.SecureRandom;
public interface TlsContext
{
-
SecureRandom getSecureRandom();
SecurityParameters getSecurityParameters();
@@ -15,6 +14,16 @@ public interface TlsContext
ProtocolVersion getServerVersion();
+ /**
+ * Used to get the resumable session, if any, used by this connection. Only available after the
+ * handshake has successfully completed.
+ *
+ * @return A {@link TlsSession} representing the resumable session used by this connection, or
+ * null if no resumable session available.
+ * @see {@link TlsPeer#notifyHandshakeComplete()}
+ */
+ TlsSession getResumableSession();
+
Object getUserObject();
void setUserObject(Object userObject);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
index 5737659..0abaee6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
@@ -1,24 +1,17 @@
package org.bouncycastle.crypto.tls;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.math.BigInteger;
import java.util.Vector;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
-import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
-import org.bouncycastle.crypto.io.SignerInputStream;
-import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
import org.bouncycastle.crypto.params.DHParameters;
-import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.util.io.TeeInputStream;
public class TlsDHEKeyExchange
extends TlsDHKeyExchange
{
-
protected TlsSignerCredentials serverCredentials = null;
public TlsDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
@@ -29,7 +22,6 @@ public class TlsDHEKeyExchange
public void processServerCredentials(TlsCredentials serverCredentials)
throws IOException
{
-
if (!(serverCredentials instanceof TlsSignerCredentials))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -43,40 +35,50 @@ public class TlsDHEKeyExchange
public byte[] generateServerKeyExchange()
throws IOException
{
-
if (this.dhParameters == null)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ DigestInputBuffer buf = new DigestInputBuffer();
- DHKeyPairGenerator kpg = new DHKeyPairGenerator();
- kpg.init(new DHKeyGenerationParameters(context.getSecureRandom(), this.dhParameters));
- AsymmetricCipherKeyPair kp = kpg.generateKeyPair();
+ this.dhAgreeServerPrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(),
+ this.dhParameters, buf);
- BigInteger Ys = ((DHPublicKeyParameters)kp.getPublic()).getY();
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+ Digest d;
- TlsDHUtils.writeDHParameter(dhParameters.getP(), buf);
- TlsDHUtils.writeDHParameter(dhParameters.getG(), buf);
- TlsDHUtils.writeDHParameter(Ys, buf);
+ if (TlsUtils.isTLSv12(context))
+ {
+ signatureAndHashAlgorithm = serverCredentials.getSignatureAndHashAlgorithm();
+ if (signatureAndHashAlgorithm == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
- byte[] digestInput = buf.toByteArray();
+ d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash());
+ }
+ else
+ {
+ signatureAndHashAlgorithm = null;
+ d = new CombinedHash();
+ }
- Digest d = new CombinedHash();
SecurityParameters securityParameters = context.getSecurityParameters();
d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
- d.update(digestInput, 0, digestInput.length);
+ buf.updateDigest(d);
byte[] hash = new byte[d.getDigestSize()];
d.doFinal(hash, 0);
- byte[] sigBytes = serverCredentials.generateCertificateSignature(hash);
- /*
- * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended from TLS 1.2
- */
- TlsUtils.writeOpaque16(sigBytes, buf);
+ byte[] signature = serverCredentials.generateCertificateSignature(hash);
+
+ DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ signed_params.encode(buf);
return buf.toByteArray();
}
@@ -84,28 +86,28 @@ public class TlsDHEKeyExchange
public void processServerKeyExchange(InputStream input)
throws IOException
{
-
SecurityParameters securityParameters = context.getSecurityParameters();
- Signer signer = initVerifyer(tlsSigner, securityParameters);
- InputStream sigIn = new SignerInputStream(input, signer);
+ SignerInputBuffer buf = new SignerInputBuffer();
+ InputStream teeIn = new TeeInputStream(input, buf);
+
+ ServerDHParams params = ServerDHParams.parse(teeIn);
- BigInteger p = TlsDHUtils.readDHParameter(sigIn);
- BigInteger g = TlsDHUtils.readDHParameter(sigIn);
- BigInteger Ys = TlsDHUtils.readDHParameter(sigIn);
+ DigitallySigned signed_params = DigitallySigned.parse(context, input);
- byte[] sigBytes = TlsUtils.readOpaque16(input);
- if (!signer.verifySignature(sigBytes))
+ Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters);
+ buf.updateSigner(signer);
+ if (!signer.verifySignature(signed_params.getSignature()))
{
throw new TlsFatalAlert(AlertDescription.decrypt_error);
}
- this.dhAgreeServerPublicKey = validateDHPublicKey(new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+ this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(params.getPublicKey());
}
- protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
{
- Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+ Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey);
signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
return signer;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
index 60e5105..7d79f6a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
@@ -7,7 +7,6 @@ import java.util.Vector;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
@@ -20,7 +19,6 @@ import org.bouncycastle.crypto.util.PublicKeyFactory;
public class TlsDHKeyExchange
extends AbstractTlsKeyExchange
{
-
protected static final BigInteger ONE = BigInteger.valueOf(1);
protected static final BigInteger TWO = BigInteger.valueOf(2);
@@ -32,11 +30,11 @@ public class TlsDHKeyExchange
protected TlsAgreementCredentials agreementCredentials;
protected DHPrivateKeyParameters dhAgreeClientPrivateKey;
+ protected DHPrivateKeyParameters dhAgreeServerPrivateKey;
protected DHPublicKeyParameters dhAgreeClientPublicKey;
public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
{
-
super(keyExchange, supportedSignatureAlgorithms);
switch (keyExchange)
@@ -77,7 +75,6 @@ public class TlsDHKeyExchange
public void processServerCertificate(Certificate serverCertificate)
throws IOException
{
-
if (serverCertificate.isEmpty())
{
throw new TlsFatalAlert(AlertDescription.bad_certificate);
@@ -99,7 +96,7 @@ public class TlsDHKeyExchange
{
try
{
- this.dhAgreeServerPublicKey = validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
+ this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
}
catch (ClassCastException e)
{
@@ -196,27 +193,16 @@ public class TlsDHKeyExchange
return agreementCredentials.generateAgreement(dhAgreeServerPublicKey);
}
- return calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
- }
-
- protected boolean areCompatibleParameters(DHParameters a, DHParameters b)
- {
- return a.getP().equals(b.getP()) && a.getG().equals(b.getG());
- }
-
- protected byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey)
- {
- return TlsDHUtils.calculateDHBasicAgreement(publicKey, privateKey);
- }
+ if (dhAgreeServerPrivateKey != null)
+ {
+ return TlsDHUtils.calculateDHBasicAgreement(dhAgreeClientPublicKey, dhAgreeServerPrivateKey);
+ }
- protected AsymmetricCipherKeyPair generateDHKeyPair(DHParameters dhParams)
- {
- return TlsDHUtils.generateDHKeyPair(context.getSecureRandom(), dhParams);
- }
+ if (dhAgreeClientPrivateKey != null)
+ {
+ return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+ }
- protected DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key)
- throws IOException
- {
- return TlsDHUtils.validateDHPublicKey(key);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
index 014e40f..748c879 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
@@ -17,14 +17,16 @@ import org.bouncycastle.util.BigIntegers;
public class TlsDHUtils
{
-
static final BigInteger ONE = BigInteger.valueOf(1);
static final BigInteger TWO = BigInteger.valueOf(2);
- public static byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey,
- DHPrivateKeyParameters privateKey)
+ public static boolean areCompatibleParameters(DHParameters a, DHParameters b)
{
+ return a.getP().equals(b.getP()) && a.getG().equals(b.getG());
+ }
+ public static byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey)
+ {
DHBasicAgreement basicAgreement = new DHBasicAgreement();
basicAgreement.init(privateKey);
BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey);
@@ -36,32 +38,37 @@ public class TlsDHUtils
return BigIntegers.asUnsignedByteArray(agreementValue);
}
- public static AsymmetricCipherKeyPair generateDHKeyPair(SecureRandom random,
- DHParameters dhParams)
+ public static AsymmetricCipherKeyPair generateDHKeyPair(SecureRandom random, DHParameters dhParams)
{
DHBasicKeyPairGenerator dhGen = new DHBasicKeyPairGenerator();
dhGen.init(new DHKeyGenerationParameters(random, dhParams));
return dhGen.generateKeyPair();
}
- public static DHPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random,
- DHParameters dhParams, OutputStream output)
- throws IOException
+ public static DHPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random, DHParameters dhParams,
+ OutputStream output) throws IOException
{
+ AsymmetricCipherKeyPair kp = generateDHKeyPair(random, dhParams);
+
+ DHPublicKeyParameters dh_public = (DHPublicKeyParameters) kp.getPublic();
+ writeDHParameter(dh_public.getY(), output);
- AsymmetricCipherKeyPair dhAgreeClientKeyPair = generateDHKeyPair(random, dhParams);
- DHPrivateKeyParameters dhAgreeClientPrivateKey = (DHPrivateKeyParameters)dhAgreeClientKeyPair
- .getPrivate();
+ return (DHPrivateKeyParameters) kp.getPrivate();
+ }
+
+ public static DHPrivateKeyParameters generateEphemeralServerKeyExchange(SecureRandom random, DHParameters dhParams,
+ OutputStream output) throws IOException
+ {
+ AsymmetricCipherKeyPair kp = TlsDHUtils.generateDHKeyPair(random, dhParams);
- BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.getPublic()).getY();
- byte[] keData = BigIntegers.asUnsignedByteArray(Yc);
- TlsUtils.writeOpaque16(keData, output);
+ DHPublicKeyParameters dhPublicKey = (DHPublicKeyParameters)kp.getPublic();
+ ServerDHParams params = new ServerDHParams(dhPublicKey);
+ params.encode(output);
- return dhAgreeClientPrivateKey;
+ return (DHPrivateKeyParameters)kp.getPrivate();
}
- public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key)
- throws IOException
+ public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key) throws IOException
{
BigInteger Y = key.getY();
DHParameters params = key.getParameters();
@@ -86,14 +93,12 @@ public class TlsDHUtils
return key;
}
- public static BigInteger readDHParameter(InputStream input)
- throws IOException
+ public static BigInteger readDHParameter(InputStream input) throws IOException
{
return new BigInteger(1, TlsUtils.readOpaque16(input));
}
- public static void writeDHParameter(BigInteger x, OutputStream output)
- throws IOException
+ public static void writeDHParameter(BigInteger x, OutputStream output) throws IOException
{
TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(x), output);
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
index b0e8957..4cb8004 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
@@ -6,7 +6,6 @@ import org.bouncycastle.crypto.DSA;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.digests.NullDigest;
-import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.DSADigestSigner;
@@ -14,44 +13,73 @@ import org.bouncycastle.crypto.signers.DSADigestSigner;
public abstract class TlsDSASigner
extends AbstractTlsSigner
{
-
- public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+ public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm,
+ AsymmetricKeyParameter privateKey, byte[] hash)
throws CryptoException
{
-
- // Note: Only use the SHA1 part of the hash
- Signer signer = makeSigner(new NullDigest(), true,
+ Signer signer = makeSigner(algorithm, true, true,
new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
- signer.update(md5AndSha1, 16, 20);
+ if (algorithm == null)
+ {
+ // Note: Only use the SHA1 part of the (MD5/SHA1) hash
+ signer.update(hash, 16, 20);
+ }
+ else
+ {
+ signer.update(hash, 0, hash.length);
+ }
return signer.generateSignature();
}
- public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+ public boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+ AsymmetricKeyParameter publicKey, byte[] hash)
throws CryptoException
{
-
- // Note: Only use the SHA1 part of the hash
- Signer signer = makeSigner(new NullDigest(), false, publicKey);
- signer.update(md5AndSha1, 16, 20);
+ Signer signer = makeSigner(algorithm, true, false, publicKey);
+ if (algorithm == null)
+ {
+ // Note: Only use the SHA1 part of the (MD5/SHA1) hash
+ signer.update(hash, 16, 20);
+ }
+ else
+ {
+ signer.update(hash, 0, hash.length);
+ }
return signer.verifySignature(sigBytes);
}
- public Signer createSigner(AsymmetricKeyParameter privateKey)
+ public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey)
{
- return makeSigner(new SHA1Digest(), true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+ return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
}
- public Signer createVerifyer(AsymmetricKeyParameter publicKey)
+ public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey)
{
- return makeSigner(new SHA1Digest(), false, publicKey);
+ return makeSigner(algorithm, false, false, publicKey);
}
- protected Signer makeSigner(Digest d, boolean forSigning, CipherParameters cp)
+ protected Signer makeSigner(SignatureAndHashAlgorithm algorithm, boolean raw, boolean forSigning,
+ CipherParameters cp)
{
+ if ((algorithm != null) != TlsUtils.isTLSv12(context))
+ {
+ throw new IllegalStateException();
+ }
+
+ if (algorithm != null
+ && (algorithm.getHash() != HashAlgorithm.sha1 || algorithm.getSignature() != getSignatureAlgorithm()))
+ {
+ throw new IllegalStateException();
+ }
+
+ Digest d = raw ? new NullDigest() : TlsUtils.createHash(HashAlgorithm.sha1);
+
Signer s = new DSADigestSigner(createDSAImpl(), d);
s.init(forSigning, cp);
return s;
}
+ protected abstract short getSignatureAlgorithm();
+
protected abstract DSA createDSAImpl();
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
index e0eeca9..cb698bf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
@@ -8,7 +8,6 @@ import org.bouncycastle.crypto.signers.DSASigner;
public class TlsDSSSigner
extends TlsDSASigner
{
-
public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
{
return publicKey instanceof DSAPublicKeyParameters;
@@ -18,4 +17,9 @@ public class TlsDSSSigner
{
return new DSASigner();
}
+
+ protected short getSignatureAlgorithm()
+ {
+ return SignatureAlgorithm.dsa;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
index a49f83f..3e7ef39 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
@@ -1,7 +1,6 @@
package org.bouncycastle.crypto.tls;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -9,7 +8,7 @@ import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Hashtable;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
@@ -19,91 +18,63 @@ import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Integers;
public class TlsECCUtils
{
-
public static final Integer EXT_elliptic_curves = Integers.valueOf(ExtensionType.elliptic_curves);
public static final Integer EXT_ec_point_formats = Integers.valueOf(ExtensionType.ec_point_formats);
- private static final String[] curveNames = new String[]{"sect163k1", "sect163r1", "sect163r2", "sect193r1",
+ private static final String[] curveNames = new String[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
"sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
"sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
- "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",};
+ "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",
+ "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"};
- public static void addSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves)
- throws IOException
+ public static void addSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves) throws IOException
{
-
extensions.put(EXT_elliptic_curves, createSupportedEllipticCurvesExtension(namedCurves));
}
public static void addSupportedPointFormatsExtension(Hashtable extensions, short[] ecPointFormats)
throws IOException
{
-
extensions.put(EXT_ec_point_formats, createSupportedPointFormatsExtension(ecPointFormats));
}
- public static int[] getSupportedEllipticCurvesExtension(Hashtable extensions)
- throws IOException
+ public static int[] getSupportedEllipticCurvesExtension(Hashtable extensions) throws IOException
{
-
- if (extensions == null)
- {
- return null;
- }
- byte[] extensionValue = (byte[])extensions.get(EXT_elliptic_curves);
- if (extensionValue == null)
- {
- return null;
- }
- return readSupportedEllipticCurvesExtension(extensionValue);
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_elliptic_curves);
+ return extensionData == null ? null : readSupportedEllipticCurvesExtension(extensionData);
}
- public static short[] getSupportedPointFormatsExtension(Hashtable extensions)
- throws IOException
+ public static short[] getSupportedPointFormatsExtension(Hashtable extensions) throws IOException
{
-
- if (extensions == null)
- {
- return null;
- }
- byte[] extensionValue = (byte[])extensions.get(EXT_ec_point_formats);
- if (extensionValue == null)
- {
- return null;
- }
- return readSupportedPointFormatsExtension(extensionValue);
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_ec_point_formats);
+ return extensionData == null ? null : readSupportedPointFormatsExtension(extensionData);
}
- public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves)
- throws IOException
+ public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves) throws IOException
{
-
if (namedCurves == null || namedCurves.length < 1)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeUint16(2 * namedCurves.length, buf);
- TlsUtils.writeUint16Array(namedCurves, buf);
- return buf.toByteArray();
+ return TlsUtils.encodeUint16ArrayWithUint16Length(namedCurves);
}
- public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats)
- throws IOException
+ public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats) throws IOException
{
-
if (ecPointFormats == null)
{
- ecPointFormats = new short[]{ECPointFormat.uncompressed};
+ ecPointFormats = new short[] { ECPointFormat.uncompressed };
}
- else if (!TlsProtocol.arrayContains(ecPointFormats, ECPointFormat.uncompressed))
+ else if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed))
{
/*
* RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
@@ -118,22 +89,17 @@ public class TlsECCUtils
ecPointFormats = tmp;
}
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeUint8((short)ecPointFormats.length, buf);
- TlsUtils.writeUint8Array(ecPointFormats, buf);
- return buf.toByteArray();
+ return TlsUtils.encodeUint8ArrayWithUint8Length(ecPointFormats);
}
- public static int[] readSupportedEllipticCurvesExtension(byte[] extensionValue)
- throws IOException
+ public static int[] readSupportedEllipticCurvesExtension(byte[] extensionData) throws IOException
{
-
- if (extensionValue == null)
+ if (extensionData == null)
{
- throw new IllegalArgumentException("'extensionValue' cannot be null");
+ throw new IllegalArgumentException("'extensionData' cannot be null");
}
- ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
int length = TlsUtils.readUint16(buf);
if (length < 2 || (length & 1) != 0)
@@ -148,16 +114,14 @@ public class TlsECCUtils
return namedCurves;
}
- public static short[] readSupportedPointFormatsExtension(byte[] extensionValue)
- throws IOException
+ public static short[] readSupportedPointFormatsExtension(byte[] extensionData) throws IOException
{
-
- if (extensionValue == null)
+ if (extensionData == null)
{
- throw new IllegalArgumentException("'extensionValue' cannot be null");
+ throw new IllegalArgumentException("'extensionData' cannot be null");
}
- ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
short length = TlsUtils.readUint8(buf);
if (length < 1)
@@ -169,7 +133,7 @@ public class TlsECCUtils
TlsProtocol.assertEmpty(buf);
- if (!TlsProtocol.arrayContains(ecPointFormats, ECPointFormat.uncompressed))
+ if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed))
{
/*
* RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
@@ -195,7 +159,7 @@ public class TlsECCUtils
}
// Lazily created the first time a particular curve is accessed
- X9ECParameters ecP = SECNamedCurves.getByName(curveName);
+ X9ECParameters ecP = ECNamedCurveTable.getByName(curveName);
if (ecP == null)
{
@@ -227,6 +191,9 @@ public class TlsECCUtils
{
switch (cipherSuite)
{
+ /*
+ * RFC 4492
+ */
case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
@@ -252,6 +219,10 @@ public class TlsECCUtils
case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+
+ /*
+ * RFC 5289
+ */
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
@@ -268,7 +239,38 @@ public class TlsECCUtils
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+
+ /*
+ * RFC 5489
+ */
+ case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+
+ /*
+ * draft-josefsson-salsa20-tls-02
+ */
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
+
return true;
+
default:
return false;
}
@@ -307,17 +309,13 @@ public class TlsECCUtils
return false;
}
- public static byte[] serializeECFieldElement(int fieldSize, BigInteger x)
- throws IOException
+ public static byte[] serializeECFieldElement(int fieldSize, BigInteger x) throws IOException
{
- int requiredLength = (fieldSize + 7) / 8;
- return BigIntegers.asUnsignedByteArray(requiredLength, x);
+ return BigIntegers.asUnsignedByteArray((fieldSize + 7) / 8, x);
}
- public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point)
- throws IOException
+ public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point) throws IOException
{
-
ECCurve curve = point.getCurve();
/*
@@ -341,12 +339,10 @@ public class TlsECCUtils
public static byte[] serializeECPublicKey(short[] ecPointFormats, ECPublicKeyParameters keyParameters)
throws IOException
{
-
return serializeECPoint(ecPointFormats, keyParameters.getQ());
}
- public static BigInteger deserializeECFieldElement(int fieldSize, byte[] encoding)
- throws IOException
+ public static BigInteger deserializeECFieldElement(int fieldSize, byte[] encoding) throws IOException
{
int requiredLength = (fieldSize + 7) / 8;
if (encoding.length != requiredLength)
@@ -356,22 +352,20 @@ public class TlsECCUtils
return new BigInteger(1, encoding);
}
- public static ECPoint deserializeECPoint(short[] ecPointFormats, ECCurve curve, byte[] encoding)
- throws IOException
+ public static ECPoint deserializeECPoint(short[] ecPointFormats, ECCurve curve, byte[] encoding) throws IOException
{
/*
* NOTE: Here we implicitly decode compressed or uncompressed encodings. DefaultTlsClient by
* default is set up to advertise that we can parse any encoding so this works fine, but
* extra checks might be needed here if that were changed.
*/
+ // TODO Review handling of infinity and hybrid encodings
return curve.decodePoint(encoding);
}
public static ECPublicKeyParameters deserializeECPublicKey(short[] ecPointFormats, ECDomainParameters curve_params,
- byte[] encoding)
- throws IOException
+ byte[] encoding) throws IOException
{
-
try
{
ECPoint Y = deserializeECPoint(ecPointFormats, curve_params.getCurve(), encoding);
@@ -385,7 +379,6 @@ public class TlsECCUtils
public static byte[] calculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey)
{
-
ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
basicAgreement.init(privateKey);
BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey);
@@ -400,22 +393,29 @@ public class TlsECCUtils
public static AsymmetricCipherKeyPair generateECKeyPair(SecureRandom random, ECDomainParameters ecParams)
{
-
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
- ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams, random);
- keyPairGenerator.init(keyGenerationParameters);
+ keyPairGenerator.init(new ECKeyGenerationParameters(ecParams, random));
return keyPairGenerator.generateKeyPair();
}
- public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key)
- throws IOException
+ public static ECPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random, short[] ecPointFormats,
+ ECDomainParameters ecParams, OutputStream output) throws IOException
+ {
+ AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(random, ecParams);
+
+ ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic();
+ writeECPoint(ecPointFormats, ecPublicKey.getQ(), output);
+
+ return (ECPrivateKeyParameters) kp.getPrivate();
+ }
+
+ public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key) throws IOException
{
// TODO Check RFC 4492 for validation
return key;
}
- public static int readECExponent(int fieldSize, InputStream input)
- throws IOException
+ public static int readECExponent(int fieldSize, InputStream input) throws IOException
{
BigInteger K = readECParameter(input);
if (K.bitLength() < 32)
@@ -429,14 +429,12 @@ public class TlsECCUtils
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- public static BigInteger readECFieldElement(int fieldSize, InputStream input)
- throws IOException
+ public static BigInteger readECFieldElement(int fieldSize, InputStream input) throws IOException
{
return deserializeECFieldElement(fieldSize, TlsUtils.readOpaque8(input));
}
- public static BigInteger readECParameter(InputStream input)
- throws IOException
+ public static BigInteger readECParameter(InputStream input) throws IOException
{
// TODO Are leading zeroes okay here?
return new BigInteger(1, TlsUtils.readOpaque8(input));
@@ -445,7 +443,6 @@ public class TlsECCUtils
public static ECDomainParameters readECParameters(int[] namedCurves, short[] ecPointFormats, InputStream input)
throws IOException
{
-
try
{
short curveType = TlsUtils.readUint8(input);
@@ -454,6 +451,8 @@ public class TlsECCUtils
{
case ECCurveType.explicit_prime:
{
+ checkNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves);
+
BigInteger prime_p = readECParameter(input);
BigInteger a = readECFieldElement(prime_p.bitLength(), input);
BigInteger b = readECFieldElement(prime_p.bitLength(), input);
@@ -465,11 +464,12 @@ public class TlsECCUtils
}
case ECCurveType.explicit_char2:
{
+ checkNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves);
+
int m = TlsUtils.readUint16(input);
short basis = TlsUtils.readUint8(input);
ECCurve curve;
- switch (basis)
- {
+ switch (basis) {
case ECBasisType.ec_basis_trinomial:
{
int k = readECExponent(m, input);
@@ -509,15 +509,7 @@ public class TlsECCUtils
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- if (!TlsProtocol.arrayContains(namedCurves, namedCurve))
- {
- /*
- * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite
- * unless they can complete the handshake while respecting the choice of curves
- * and compression techniques specified by the client.
- */
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
+ checkNamedCurve(namedCurves, namedCurve);
return TlsECCUtils.getParametersForNamedCurve(namedCurve);
}
@@ -531,47 +523,59 @@ public class TlsECCUtils
}
}
- public static void writeECExponent(int k, OutputStream output)
- throws IOException
+ private static void checkNamedCurve(int[] namedCurves, int namedCurve) throws IOException
+ {
+ if (namedCurves != null && !Arrays.contains(namedCurves, namedCurve))
+ {
+ /*
+ * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite
+ * unless they can complete the handshake while respecting the choice of curves
+ * and compression techniques specified by the client.
+ */
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ public static void writeECExponent(int k, OutputStream output) throws IOException
{
BigInteger K = BigInteger.valueOf(k);
writeECParameter(K, output);
}
- public static void writeECFieldElement(int fieldSize, BigInteger x, OutputStream output)
- throws IOException
+ public static void writeECFieldElement(ECFieldElement x, OutputStream output) throws IOException
+ {
+ TlsUtils.writeOpaque8(x.getEncoded(), output);
+ }
+
+ public static void writeECFieldElement(int fieldSize, BigInteger x, OutputStream output) throws IOException
{
TlsUtils.writeOpaque8(serializeECFieldElement(fieldSize, x), output);
}
- public static void writeECParameter(BigInteger x, OutputStream output)
- throws IOException
+ public static void writeECParameter(BigInteger x, OutputStream output) throws IOException
{
TlsUtils.writeOpaque8(BigIntegers.asUnsignedByteArray(x), output);
}
public static void writeExplicitECParameters(short[] ecPointFormats, ECDomainParameters ecParameters,
- OutputStream output)
- throws IOException
+ OutputStream output) throws IOException
{
-
ECCurve curve = ecParameters.getCurve();
if (curve instanceof ECCurve.Fp)
{
-
TlsUtils.writeUint8(ECCurveType.explicit_prime, output);
- ECCurve.Fp fp = (ECCurve.Fp)curve;
+ ECCurve.Fp fp = (ECCurve.Fp) curve;
writeECParameter(fp.getQ(), output);
-
}
else if (curve instanceof ECCurve.F2m)
{
-
TlsUtils.writeUint8(ECCurveType.explicit_char2, output);
- ECCurve.F2m f2m = (ECCurve.F2m)curve;
- TlsUtils.writeUint16(f2m.getM(), output);
+ ECCurve.F2m f2m = (ECCurve.F2m) curve;
+ int m = f2m.getM();
+ TlsUtils.checkUint16(m);
+ TlsUtils.writeUint16(m, output);
if (f2m.isTrinomial())
{
@@ -592,17 +596,20 @@ public class TlsECCUtils
throw new IllegalArgumentException("'ecParameters' not a known curve type");
}
- writeECFieldElement(curve.getFieldSize(), curve.getA().toBigInteger(), output);
- writeECFieldElement(curve.getFieldSize(), curve.getB().toBigInteger(), output);
+ writeECFieldElement(curve.getA(), output);
+ writeECFieldElement(curve.getB(), output);
TlsUtils.writeOpaque8(serializeECPoint(ecPointFormats, ecParameters.getG()), output);
writeECParameter(ecParameters.getN(), output);
writeECParameter(ecParameters.getH(), output);
}
- public static void writeNamedECParameters(int namedCurve, OutputStream output)
- throws IOException
+ public static void writeECPoint(short[] ecPointFormats, ECPoint point, OutputStream output) throws IOException
{
+ TlsUtils.writeOpaque8(TlsECCUtils.serializeECPoint(ecPointFormats, point), output);
+ }
+ public static void writeNamedECParameters(int namedCurve, OutputStream output) throws IOException
+ {
if (!NamedCurve.refersToASpecificNamedCurve(namedCurve))
{
/*
@@ -614,6 +621,7 @@ public class TlsECCUtils
}
TlsUtils.writeUint8(ECCurveType.named_curve, output);
+ TlsUtils.checkUint16(namedCurve);
TlsUtils.writeUint16(namedCurve, output);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
index 1124560..d346ef4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
@@ -1,6 +1,5 @@
package org.bouncycastle.crypto.tls;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
@@ -8,10 +7,11 @@ import java.util.Vector;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
-import org.bouncycastle.crypto.io.SignerInputStream;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.TeeInputStream;
/**
* ECDHE key exchange (see RFC 4492)
@@ -19,11 +19,10 @@ import org.bouncycastle.crypto.params.ECPublicKeyParameters;
public class TlsECDHEKeyExchange
extends TlsECDHKeyExchange
{
-
protected TlsSignerCredentials serverCredentials = null;
public TlsECDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves,
- short[] clientECPointFormats, short[] serverECPointFormats)
+ short[] clientECPointFormats, short[] serverECPointFormats)
{
super(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats);
}
@@ -31,7 +30,6 @@ public class TlsECDHEKeyExchange
public void processServerCredentials(TlsCredentials serverCredentials)
throws IOException
{
-
if (!(serverCredentials instanceof TlsSignerCredentials))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -45,13 +43,13 @@ public class TlsECDHEKeyExchange
public byte[] generateServerKeyExchange()
throws IOException
{
-
/*
* First we try to find a supported named curve from the client's list.
*/
int namedCurve = -1;
if (namedCurves == null)
{
+ // TODO Let the peer choose the default named curve
namedCurve = NamedCurve.secp256r1;
}
else
@@ -77,13 +75,13 @@ public class TlsECDHEKeyExchange
/*
* If no named curves are suitable, check if the client supports explicit curves.
*/
- if (TlsProtocol.arrayContains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
+ if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
{
curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.secp256r1);
}
- else if (TlsProtocol.arrayContains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
+ else if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
{
- curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect233r1);
+ curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect283r1);
}
}
@@ -97,12 +95,9 @@ public class TlsECDHEKeyExchange
}
AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(context.getSecureRandom(), curve_params);
- this.ecAgreeServerPrivateKey = (ECPrivateKeyParameters)kp.getPrivate();
+ this.ecAgreePrivateKey = (ECPrivateKeyParameters)kp.getPrivate();
- byte[] publicBytes = TlsECCUtils.serializeECPublicKey(clientECPointFormats,
- (ECPublicKeyParameters)kp.getPublic());
-
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ DigestInputBuffer buf = new DigestInputBuffer();
if (namedCurve < 0)
{
@@ -113,25 +108,43 @@ public class TlsECDHEKeyExchange
TlsECCUtils.writeNamedECParameters(namedCurve, buf);
}
- TlsUtils.writeOpaque8(publicBytes, buf);
+ ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic();
+ TlsECCUtils.writeECPoint(clientECPointFormats, ecPublicKey.getQ(), buf);
+
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+ Digest d;
+
+ if (TlsUtils.isTLSv12(context))
+ {
+ signatureAndHashAlgorithm = serverCredentials.getSignatureAndHashAlgorithm();
+ if (signatureAndHashAlgorithm == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
- byte[] digestInput = buf.toByteArray();
+ d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash());
+ }
+ else
+ {
+ signatureAndHashAlgorithm = null;
+ d = new CombinedHash();
+ }
- Digest d = new CombinedHash();
SecurityParameters securityParameters = context.getSecurityParameters();
d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
- d.update(digestInput, 0, digestInput.length);
+ buf.updateDigest(d);
byte[] hash = new byte[d.getDigestSize()];
d.doFinal(hash, 0);
- byte[] sigBytes = serverCredentials.generateCertificateSignature(hash);
- /*
- * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended
- * from TLS 1.2
- */
- TlsUtils.writeOpaque16(sigBytes, buf);
+ byte[] signature = serverCredentials.generateCertificateSignature(hash);
+
+ DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ signed_params.encode(buf);
return buf.toByteArray();
}
@@ -139,23 +152,25 @@ public class TlsECDHEKeyExchange
public void processServerKeyExchange(InputStream input)
throws IOException
{
-
SecurityParameters securityParameters = context.getSecurityParameters();
- Signer signer = initVerifyer(tlsSigner, securityParameters);
- InputStream sigIn = new SignerInputStream(input, signer);
+ SignerInputBuffer buf = new SignerInputBuffer();
+ InputStream teeIn = new TeeInputStream(input, buf);
+
+ ECDomainParameters curve_params = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, teeIn);
- ECDomainParameters curve_params = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, sigIn);
+ byte[] point = TlsUtils.readOpaque8(teeIn);
- byte[] point = TlsUtils.readOpaque8(sigIn);
+ DigitallySigned signed_params = DigitallySigned.parse(context, input);
- byte[] sigByte = TlsUtils.readOpaque16(input);
- if (!signer.verifySignature(sigByte))
+ Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters);
+ buf.updateSigner(signer);
+ if (!signer.verifySignature(signed_params.getSignature()))
{
throw new TlsFatalAlert(AlertDescription.decrypt_error);
}
- this.ecAgreeServerPublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
+ this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
clientECPointFormats, curve_params, point));
}
@@ -196,9 +211,9 @@ public class TlsECDHEKeyExchange
}
}
- protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
{
- Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+ Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey);
signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
return signer;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
index 26c0975..9456352 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
@@ -7,7 +7,6 @@ import java.util.Vector;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -17,26 +16,21 @@ import org.bouncycastle.crypto.util.PublicKeyFactory;
/**
* ECDH key exchange (see RFC 4492)
*/
-public class TlsECDHKeyExchange
- extends AbstractTlsKeyExchange
+public class TlsECDHKeyExchange extends AbstractTlsKeyExchange
{
-
protected TlsSigner tlsSigner;
protected int[] namedCurves;
protected short[] clientECPointFormats, serverECPointFormats;
protected AsymmetricKeyParameter serverPublicKey;
- protected ECPublicKeyParameters ecAgreeServerPublicKey;
protected TlsAgreementCredentials agreementCredentials;
- protected ECPrivateKeyParameters ecAgreeClientPrivateKey;
- protected ECPrivateKeyParameters ecAgreeServerPrivateKey;
- protected ECPublicKeyParameters ecAgreeClientPublicKey;
+ protected ECPrivateKeyParameters ecAgreePrivateKey;
+ protected ECPublicKeyParameters ecAgreePublicKey;
public TlsECDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves,
- short[] clientECPointFormats, short[] serverECPointFormats)
+ short[] clientECPointFormats, short[] serverECPointFormats)
{
-
super(keyExchange, supportedSignatureAlgorithms);
switch (keyExchange)
@@ -71,16 +65,13 @@ public class TlsECDHKeyExchange
}
}
- public void skipServerCredentials()
- throws IOException
+ public void skipServerCredentials() throws IOException
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- public void processServerCertificate(Certificate serverCertificate)
- throws IOException
+ public void processServerCertificate(Certificate serverCertificate) throws IOException
{
-
if (serverCertificate.isEmpty())
{
throw new TlsFatalAlert(AlertDescription.bad_certificate);
@@ -102,8 +93,7 @@ public class TlsECDHKeyExchange
{
try
{
- this.ecAgreeServerPublicKey = TlsECCUtils
- .validateECPublicKey((ECPublicKeyParameters)this.serverPublicKey);
+ this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey((ECPublicKeyParameters) this.serverPublicKey);
}
catch (ClassCastException e)
{
@@ -138,8 +128,7 @@ public class TlsECDHKeyExchange
}
}
- public void validateCertificateRequest(CertificateRequest certificateRequest)
- throws IOException
+ public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException
{
/*
* RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
@@ -164,14 +153,13 @@ public class TlsECDHKeyExchange
}
}
- public void processClientCredentials(TlsCredentials clientCredentials)
- throws IOException
+ public void processClientCredentials(TlsCredentials clientCredentials) throws IOException
{
if (clientCredentials instanceof TlsAgreementCredentials)
{
// TODO Validate client cert has matching parameters (see 'TlsECCUtils.areOnSameCurve')?
- this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+ this.agreementCredentials = (TlsAgreementCredentials) clientCredentials;
}
else if (clientCredentials instanceof TlsSignerCredentials)
{
@@ -183,37 +171,24 @@ public class TlsECDHKeyExchange
}
}
- public void generateClientKeyExchange(OutputStream output)
- throws IOException
+ public void generateClientKeyExchange(OutputStream output) throws IOException
{
- if (agreementCredentials != null)
+ if (agreementCredentials == null)
{
- return;
+ this.ecAgreePrivateKey = TlsECCUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
+ serverECPointFormats, ecAgreePublicKey.getParameters(), output);
}
-
- AsymmetricCipherKeyPair ecAgreeClientKeyPair = TlsECCUtils.generateECKeyPair(context.getSecureRandom(),
- ecAgreeServerPublicKey.getParameters());
- this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.getPrivate();
-
- byte[] point = TlsECCUtils.serializeECPublicKey(serverECPointFormats,
- (ECPublicKeyParameters)ecAgreeClientKeyPair.getPublic());
-
- TlsUtils.writeOpaque8(point, output);
}
- public void processClientCertificate(Certificate clientCertificate)
- throws IOException
+ public void processClientCertificate(Certificate clientCertificate) throws IOException
{
-
// TODO Extract the public key
// TODO If the certificate is 'fixed', take the public key as ecAgreeClientPublicKey
}
- public void processClientKeyExchange(InputStream input)
- throws IOException
+ public void processClientKeyExchange(InputStream input) throws IOException
{
-
- if (ecAgreeClientPublicKey != null)
+ if (ecAgreePublicKey != null)
{
// For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate
return;
@@ -221,28 +196,22 @@ public class TlsECDHKeyExchange
byte[] point = TlsUtils.readOpaque8(input);
- ECDomainParameters curve_params = this.ecAgreeServerPrivateKey.getParameters();
+ ECDomainParameters curve_params = this.ecAgreePrivateKey.getParameters();
- this.ecAgreeClientPublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
+ this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
serverECPointFormats, curve_params, point));
}
- public byte[] generatePremasterSecret()
- throws IOException
+ public byte[] generatePremasterSecret() throws IOException
{
if (agreementCredentials != null)
{
- return agreementCredentials.generateAgreement(ecAgreeServerPublicKey);
- }
-
- if (ecAgreeServerPrivateKey != null)
- {
- return TlsECCUtils.calculateECDHBasicAgreement(ecAgreeClientPublicKey, ecAgreeServerPrivateKey);
+ return agreementCredentials.generateAgreement(ecAgreePublicKey);
}
- if (ecAgreeClientPrivateKey != null)
+ if (ecAgreePrivateKey != null)
{
- return TlsECCUtils.calculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey);
+ return TlsECCUtils.calculateECDHBasicAgreement(ecAgreePublicKey, ecAgreePrivateKey);
}
throw new TlsFatalAlert(AlertDescription.internal_error);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
index 6809815..d7f8064 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
@@ -8,7 +8,6 @@ import org.bouncycastle.crypto.signers.ECDSASigner;
public class TlsECDSASigner
extends TlsDSASigner
{
-
public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
{
return publicKey instanceof ECPublicKeyParameters;
@@ -18,4 +17,9 @@ public class TlsECDSASigner
{
return new ECDSASigner();
}
+
+ protected short getSignatureAlgorithm()
+ {
+ return SignatureAlgorithm.ecdsa;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
index 2680136..eddf684 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
@@ -5,7 +5,6 @@ import java.io.IOException;
public interface TlsEncryptionCredentials
extends TlsCredentials
{
-
byte[] decryptPreMasterSecret(byte[] encryptedPreMasterSecret)
throws IOException;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java
new file mode 100644
index 0000000..fbc39dd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java
@@ -0,0 +1,240 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.util.Integers;
+
+public class TlsExtensionsUtils
+{
+ public static final Integer EXT_heartbeat = Integers.valueOf(ExtensionType.heartbeat);
+ public static final Integer EXT_max_fragment_length = Integers.valueOf(ExtensionType.max_fragment_length);
+ public static final Integer EXT_server_name = Integers.valueOf(ExtensionType.server_name);
+ public static final Integer EXT_status_request = Integers.valueOf(ExtensionType.status_request);
+ public static final Integer EXT_truncated_hmac = Integers.valueOf(ExtensionType.truncated_hmac);
+
+ public static Hashtable ensureExtensionsInitialised(Hashtable extensions)
+ {
+ return extensions == null ? new Hashtable() : extensions;
+ }
+
+ public static void addHeartbeatExtension(Hashtable extensions, HeartbeatExtension heartbeatExtension)
+ throws IOException
+ {
+ extensions.put(EXT_heartbeat, createHeartbeatExtension(heartbeatExtension));
+ }
+
+ public static void addMaxFragmentLengthExtension(Hashtable extensions, short maxFragmentLength)
+ throws IOException
+ {
+ extensions.put(EXT_max_fragment_length, createMaxFragmentLengthExtension(maxFragmentLength));
+ }
+
+ public static void addServerNameExtension(Hashtable extensions, ServerNameList serverNameList)
+ throws IOException
+ {
+ extensions.put(EXT_server_name, createServerNameExtension(serverNameList));
+ }
+
+ public static void addStatusRequestExtension(Hashtable extensions, CertificateStatusRequest statusRequest)
+ throws IOException
+ {
+ extensions.put(EXT_status_request, createStatusRequestExtension(statusRequest));
+ }
+
+ public static void addTruncatedHMacExtension(Hashtable extensions)
+ {
+ extensions.put(EXT_truncated_hmac, createTruncatedHMacExtension());
+ }
+
+ public static HeartbeatExtension getHeartbeatExtension(Hashtable extensions)
+ throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_heartbeat);
+ return extensionData == null ? null : readHeartbeatExtension(extensionData);
+ }
+
+ public static short getMaxFragmentLengthExtension(Hashtable extensions)
+ throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_max_fragment_length);
+ return extensionData == null ? -1 : readMaxFragmentLengthExtension(extensionData);
+ }
+
+ public static ServerNameList getServerNameExtension(Hashtable extensions)
+ throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_server_name);
+ return extensionData == null ? null : readServerNameExtension(extensionData);
+ }
+
+ public static CertificateStatusRequest getStatusRequestExtension(Hashtable extensions)
+ throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_status_request);
+ return extensionData == null ? null : readStatusRequestExtension(extensionData);
+ }
+
+ public static boolean hasTruncatedHMacExtension(Hashtable extensions) throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_truncated_hmac);
+ return extensionData == null ? false : readTruncatedHMacExtension(extensionData);
+ }
+
+ public static byte[] createEmptyExtensionData()
+ {
+ return TlsUtils.EMPTY_BYTES;
+ }
+
+ public static byte[] createHeartbeatExtension(HeartbeatExtension heartbeatExtension)
+ throws IOException
+ {
+ if (heartbeatExtension == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ heartbeatExtension.encode(buf);
+
+ return buf.toByteArray();
+ }
+
+ public static byte[] createMaxFragmentLengthExtension(short maxFragmentLength)
+ throws IOException
+ {
+ if (!MaxFragmentLength.isValid(maxFragmentLength))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return new byte[]{ (byte)maxFragmentLength };
+ }
+
+ public static byte[] createServerNameExtension(ServerNameList serverNameList)
+ throws IOException
+ {
+ if (serverNameList == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ serverNameList.encode(buf);
+
+ return buf.toByteArray();
+ }
+
+ public static byte[] createStatusRequestExtension(CertificateStatusRequest statusRequest)
+ throws IOException
+ {
+ if (statusRequest == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ statusRequest.encode(buf);
+
+ return buf.toByteArray();
+ }
+
+ public static byte[] createTruncatedHMacExtension()
+ {
+ return createEmptyExtensionData();
+ }
+
+ public static HeartbeatExtension readHeartbeatExtension(byte[] extensionData)
+ throws IOException
+ {
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+
+ HeartbeatExtension heartbeatExtension = HeartbeatExtension.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ return heartbeatExtension;
+ }
+
+ public static short readMaxFragmentLengthExtension(byte[] extensionData)
+ throws IOException
+ {
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ if (extensionData.length != 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ short maxFragmentLength = (short)extensionData[0];
+
+ if (!MaxFragmentLength.isValid(maxFragmentLength))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return maxFragmentLength;
+ }
+
+ public static ServerNameList readServerNameExtension(byte[] extensionData)
+ throws IOException
+ {
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+
+ ServerNameList serverNameList = ServerNameList.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ return serverNameList;
+ }
+
+ public static CertificateStatusRequest readStatusRequestExtension(byte[] extensionData)
+ throws IOException
+ {
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+
+ CertificateStatusRequest statusRequest = CertificateStatusRequest.parse(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ return statusRequest;
+ }
+
+ private static boolean readTruncatedHMacExtension(byte[] extensionData) throws IOException
+ {
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ if (extensionData.length != 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return true;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
index b17b8d7..1cb0f4d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
@@ -5,10 +5,17 @@ import org.bouncycastle.crypto.Digest;
interface TlsHandshakeHash
extends Digest
{
-
void init(TlsContext context);
- TlsHandshakeHash commit();
+ TlsHandshakeHash notifyPRFDetermined();
+
+ void trackHashAlgorithm(short hashAlgorithm);
+
+ void sealHashAlgorithms();
+
+ TlsHandshakeHash stopTracking();
+
+ Digest forkPRFHash();
- TlsHandshakeHash fork();
+ byte[] getFinalHash(short hashAlgorithm);
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
index ec11130..20dfef8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
@@ -1,8 +1,5 @@
package org.bouncycastle.crypto.tls;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.digests.LongDigest;
@@ -15,19 +12,19 @@ import org.bouncycastle.util.Arrays;
*/
public class TlsMac
{
-
protected TlsContext context;
protected byte[] secret;
protected Mac mac;
protected int digestBlockSize;
protected int digestOverhead;
+ protected int macLength;
/**
* Generate a new instance of an TlsMac.
*
* @param context the TLS client context
* @param digest The digest to use.
- * @param key A byte-array where the key for this mac is located.
+ * @param key A byte-array where the key for this MAC is located.
* @param keyOff The number of bytes to skip, before the key starts in the buffer.
* @param len The length of the key.
*/
@@ -51,7 +48,7 @@ public class TlsMac
this.digestOverhead = 8;
}
- if (context.getServerVersion().isSSL())
+ if (TlsUtils.isSSL(context))
{
this.mac = new SSL3Mac(digest);
@@ -73,6 +70,12 @@ public class TlsMac
}
this.mac.init(keyParameter);
+
+ this.macLength = mac.getMacSize();
+ if (context.getSecurityParameters().truncatedHMac)
+ {
+ this.macLength = Math.min(this.macLength, 10);
+ }
}
/**
@@ -84,11 +87,11 @@ public class TlsMac
}
/**
- * @return The Keysize of the mac.
+ * @return The output length of this MAC.
*/
public int getSize()
{
- return mac.getMacSize();
+ return macLength;
}
/**
@@ -102,42 +105,38 @@ public class TlsMac
*/
public byte[] calculateMac(long seqNo, short type, byte[] message, int offset, int length)
{
+ /*
+ * TODO[draft-josefsson-salsa20-tls-02] 3. Moreover, in order to accommodate MAC algorithms
+ * like UMAC that require a nonce as part of their operation, the document extends the MAC
+ * algorithm as specified in the TLS protocol. The extended MAC includes a nonce as a second
+ * parameter. MAC algorithms that do not require a nonce, such as HMAC, are assumed to
+ * ignore the nonce input value. The MAC in a GenericStreamCipher is then calculated as
+ * follows.
+ */
ProtocolVersion serverVersion = context.getServerVersion();
boolean isSSL = serverVersion.isSSL();
- ByteArrayOutputStream bosMac = new ByteArrayOutputStream(isSSL ? 11 : 13);
- try
+ byte[] macHeader = new byte[isSSL ? 11 : 13];
+ TlsUtils.writeUint64(seqNo, macHeader, 0);
+ TlsUtils.writeUint8(type, macHeader, 8);
+ if (!isSSL)
{
- TlsUtils.writeUint64(seqNo, bosMac);
- TlsUtils.writeUint8(type, bosMac);
-
- if (!isSSL)
- {
- TlsUtils.writeVersion(serverVersion, bosMac);
- }
-
- TlsUtils.writeUint16(length, bosMac);
- }
- catch (IOException e)
- {
- // This should never happen
- throw new IllegalStateException("Internal error during mac calculation");
+ TlsUtils.writeVersion(serverVersion, macHeader, 9);
}
+ TlsUtils.writeUint16(length, macHeader, macHeader.length - 2);
- byte[] macHeader = bosMac.toByteArray();
mac.update(macHeader, 0, macHeader.length);
mac.update(message, offset, length);
byte[] result = new byte[mac.getMacSize()];
mac.doFinal(result, 0);
- return result;
+ return truncate(result);
}
public byte[] calculateMacConstantTime(long seqNo, short type, byte[] message, int offset, int length,
- int fullLength, byte[] dummyData)
+ int fullLength, byte[] dummyData)
{
-
/*
* Actual MAC only calculated on 'length' bytes...
*/
@@ -147,7 +146,7 @@ public class TlsMac
* ...but ensure a constant number of complete digest blocks are processed (as many as would
* be needed for 'fullLength' bytes of input).
*/
- int headerLength = context.getServerVersion().isSSL() ? 11 : 13;
+ int headerLength = TlsUtils.isSSL(context) ? 11 : 13;
// How many extra full blocks do we need to calculate?
int extra = getDigestBlockCount(headerLength + fullLength) - getDigestBlockCount(headerLength + length);
@@ -164,9 +163,19 @@ public class TlsMac
return result;
}
- private int getDigestBlockCount(int inputLength)
+ protected int getDigestBlockCount(int inputLength)
{
// NOTE: This calculation assumes a minimum of 1 pad byte
return (inputLength + digestOverhead) / digestBlockSize;
}
+
+ protected byte[] truncate(byte[] bs)
+ {
+ if (bs.length <= macLength)
+ {
+ return bs;
+ }
+
+ return Arrays.copyOf(bs, macLength);
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java
index d5b2b98..d1f6986 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java
@@ -26,7 +26,6 @@ public class TlsNullCipher
public TlsNullCipher(TlsContext context, Digest clientWriteDigest, Digest serverWriteDigest)
throws IOException
{
-
if ((clientWriteDigest == null) != (serverWriteDigest == null))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -38,7 +37,6 @@ public class TlsNullCipher
if (clientWriteDigest != null)
{
-
int key_block_size = clientWriteDigest.getDigestSize()
+ serverWriteDigest.getDigestSize();
byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size);
@@ -84,7 +82,6 @@ public class TlsNullCipher
public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
throws IOException
{
-
if (writeMac == null)
{
return Arrays.copyOfRange(plaintext, offset, offset + len);
@@ -100,7 +97,6 @@ public class TlsNullCipher
public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
throws IOException
{
-
if (readMac == null)
{
return Arrays.copyOfRange(ciphertext, offset, offset + len);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
index cfabb76..7217bac 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
@@ -4,7 +4,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.math.BigInteger;
import java.util.Vector;
import org.bouncycastle.asn1.x509.KeyUsage;
@@ -22,33 +21,42 @@ import org.bouncycastle.crypto.util.PublicKeyFactory;
public class TlsPSKKeyExchange
extends AbstractTlsKeyExchange
{
-
protected TlsPSKIdentity pskIdentity;
+ protected DHParameters dhParameters;
+ protected int[] namedCurves;
+ protected short[] clientECPointFormats, serverECPointFormats;
protected byte[] psk_identity_hint = null;
- protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
- protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+ protected DHPrivateKeyParameters dhAgreePrivateKey = null;
+ protected DHPublicKeyParameters dhAgreePublicKey = null;
protected AsymmetricKeyParameter serverPublicKey = null;
protected RSAKeyParameters rsaServerPublicKey = null;
+ protected TlsEncryptionCredentials serverCredentials = null;
protected byte[] premasterSecret;
- public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity)
+ public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity,
+ DHParameters dhParameters, int[] namedCurves, short[] clientECPointFormats, short[] serverECPointFormats)
{
super(keyExchange, supportedSignatureAlgorithms);
switch (keyExchange)
{
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
case KeyExchangeAlgorithm.PSK:
case KeyExchangeAlgorithm.RSA_PSK:
- case KeyExchangeAlgorithm.DHE_PSK:
break;
default:
throw new IllegalArgumentException("unsupported key exchange algorithm");
}
this.pskIdentity = pskIdentity;
+ this.dhParameters = dhParameters;
+ this.namedCurves = namedCurves;
+ this.clientECPointFormats = clientECPointFormats;
+ this.serverECPointFormats = serverECPointFormats;
}
public void skipServerCredentials()
@@ -60,10 +68,61 @@ public class TlsPSKKeyExchange
}
}
- public void processServerCertificate(Certificate serverCertificate)
+ public void processServerCredentials(TlsCredentials serverCredentials)
throws IOException
{
+ if (!(serverCredentials instanceof TlsEncryptionCredentials))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ processServerCertificate(serverCredentials.getCertificate());
+
+ this.serverCredentials = (TlsEncryptionCredentials)serverCredentials;
+ }
+
+ public byte[] generateServerKeyExchange() throws IOException
+ {
+ // TODO[RFC 4279] Need a server-side PSK API to determine hint and resolve identities to keys
+ this.psk_identity_hint = null;
+
+ if (this.psk_identity_hint == null && !requiresServerKeyExchange())
+ {
+ return null;
+ }
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ if (this.psk_identity_hint == null)
+ {
+ TlsUtils.writeOpaque16(TlsUtils.EMPTY_BYTES, buf);
+ }
+ else
+ {
+ TlsUtils.writeOpaque16(this.psk_identity_hint, buf);
+ }
+
+ if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ if (this.dhParameters == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(),
+ this.dhParameters, buf);
+ }
+ else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ // TODO[RFC 5489]
+ }
+
+ return buf.toByteArray();
+ }
+ public void processServerCertificate(Certificate serverCertificate)
+ throws IOException
+ {
if (keyExchange != KeyExchangeAlgorithm.RSA_PSK)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
@@ -100,27 +159,30 @@ public class TlsPSKKeyExchange
public boolean requiresServerKeyExchange()
{
- return keyExchange == KeyExchangeAlgorithm.DHE_PSK;
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.DHE_PSK:
+ case KeyExchangeAlgorithm.ECDHE_PSK:
+ return true;
+ default:
+ return false;
+ }
}
public void processServerKeyExchange(InputStream input)
throws IOException
{
-
this.psk_identity_hint = TlsUtils.readOpaque16(input);
if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
{
- byte[] pBytes = TlsUtils.readOpaque16(input);
- byte[] gBytes = TlsUtils.readOpaque16(input);
- byte[] YsBytes = TlsUtils.readOpaque16(input);
+ ServerDHParams serverDHParams = ServerDHParams.parse(input);
- BigInteger p = new BigInteger(1, pBytes);
- BigInteger g = new BigInteger(1, gBytes);
- BigInteger Ys = new BigInteger(1, YsBytes);
-
- this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Ys,
- new DHParameters(p, g)));
+ this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(serverDHParams.getPublicKey());
+ }
+ else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ // TODO[RFC 5489]
}
}
@@ -139,7 +201,6 @@ public class TlsPSKKeyExchange
public void generateClientKeyExchange(OutputStream output)
throws IOException
{
-
if (psk_identity_hint == null)
{
pskIdentity.skipIdentityHint();
@@ -153,22 +214,26 @@ public class TlsPSKKeyExchange
TlsUtils.writeOpaque16(psk_identity, output);
- if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
{
- this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey,
- output);
+ this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
+ dhAgreePublicKey.getParameters(), output);
+ }
+ else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ // TODO[RFC 5489]
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
- else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
{
- this.dhAgreeClientPrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
- dhAgreeServerPublicKey.getParameters(), output);
+ this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey,
+ output);
}
}
public byte[] generatePremasterSecret()
throws IOException
{
-
byte[] psk = pskIdentity.getPSK();
byte[] other_secret = generateOtherSecret(psk.length);
@@ -178,12 +243,22 @@ public class TlsPSKKeyExchange
return buf.toByteArray();
}
- protected byte[] generateOtherSecret(int pskLength)
+ protected byte[] generateOtherSecret(int pskLength) throws IOException
{
-
if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
{
- return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+ if (dhAgreePrivateKey != null)
+ {
+ return TlsDHUtils.calculateDHBasicAgreement(dhAgreePublicKey, dhAgreePrivateKey);
+ }
+
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ // TODO[RFC 5489]
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
index e408002..88780ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
@@ -1,7 +1,14 @@
package org.bouncycastle.crypto.tls;
+import java.io.IOException;
+
public interface TlsPeer
{
+ void notifySecureRenegotiation(boolean secureNegotiation) throws IOException;
+
+ TlsCompression getCompression() throws IOException;
+
+ TlsCipher getCipher() throws IOException;
/**
* This method will be called when an alert is raised by the protocol.
@@ -20,4 +27,9 @@ public interface TlsPeer
* @param alertDescription {@link AlertDescription}
*/
void notifyAlertReceived(short alertLevel, short alertDescription);
+
+ /**
+ * Notifies the peer that the handshake has been successfully completed.
+ */
+ void notifyHandshakeComplete() throws IOException;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
index 6d8e3d3..2c3b094 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
@@ -2,6 +2,7 @@ package org.bouncycastle.crypto.tls;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -10,6 +11,7 @@ import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
+import org.bouncycastle.crypto.Digest;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Integers;
@@ -18,7 +20,6 @@ import org.bouncycastle.util.Integers;
*/
public abstract class TlsProtocol
{
-
protected static final Integer EXT_RenegotiationInfo = Integers.valueOf(ExtensionType.renegotiation_info);
protected static final Integer EXT_SessionTicket = Integers.valueOf(ExtensionType.session_ticket);
@@ -32,25 +33,24 @@ public abstract class TlsProtocol
protected static final short CS_SERVER_HELLO = 2;
protected static final short CS_SERVER_SUPPLEMENTAL_DATA = 3;
protected static final short CS_SERVER_CERTIFICATE = 4;
- protected static final short CS_SERVER_KEY_EXCHANGE = 5;
- protected static final short CS_CERTIFICATE_REQUEST = 6;
- protected static final short CS_SERVER_HELLO_DONE = 7;
- protected static final short CS_CLIENT_SUPPLEMENTAL_DATA = 8;
- protected static final short CS_CLIENT_CERTIFICATE = 9;
- protected static final short CS_CLIENT_KEY_EXCHANGE = 10;
- protected static final short CS_CERTIFICATE_VERIFY = 11;
- protected static final short CS_CLIENT_CHANGE_CIPHER_SPEC = 12;
+ protected static final short CS_CERTIFICATE_STATUS = 5;
+ protected static final short CS_SERVER_KEY_EXCHANGE = 6;
+ protected static final short CS_CERTIFICATE_REQUEST = 7;
+ protected static final short CS_SERVER_HELLO_DONE = 8;
+ protected static final short CS_CLIENT_SUPPLEMENTAL_DATA = 9;
+ protected static final short CS_CLIENT_CERTIFICATE = 10;
+ protected static final short CS_CLIENT_KEY_EXCHANGE = 11;
+ protected static final short CS_CERTIFICATE_VERIFY = 12;
protected static final short CS_CLIENT_FINISHED = 13;
protected static final short CS_SERVER_SESSION_TICKET = 14;
- protected static final short CS_SERVER_CHANGE_CIPHER_SPEC = 15;
- protected static final short CS_SERVER_FINISHED = 16;
+ protected static final short CS_SERVER_FINISHED = 15;
+ protected static final short CS_END = 16;
/*
* Queues for data from some protocols.
*/
private ByteQueue applicationDataQueue = new ByteQueue();
- private ByteQueue changeCipherSpecQueue = new ByteQueue();
- private ByteQueue alertQueue = new ByteQueue();
+ private ByteQueue alertQueue = new ByteQueue(2);
private ByteQueue handshakeQueue = new ByteQueue();
/*
@@ -65,13 +65,24 @@ public abstract class TlsProtocol
private volatile boolean closed = false;
private volatile boolean failedWithError = false;
private volatile boolean appDataReady = false;
- private volatile boolean writeExtraEmptyRecords = true;
+ private volatile boolean splitApplicationDataRecords = true;
private byte[] expected_verify_data = null;
+ protected TlsSession tlsSession = null;
+ protected SessionParameters sessionParameters = null;
protected SecurityParameters securityParameters = null;
+ protected Certificate peerCertificate = null;
+
+ protected int[] offeredCipherSuites = null;
+ protected short[] offeredCompressionMethods = null;
+ protected Hashtable clientExtensions = null;
+ protected Hashtable serverExtensions = null;
protected short connection_state = CS_START;
+ protected boolean resumedSession = false;
+ protected boolean receivedChangeCipherSpec = false;
protected boolean secure_renegotiation = false;
+ protected boolean allowCertificateStatus = false;
protected boolean expectSessionTicket = false;
public TlsProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
@@ -84,8 +95,9 @@ public abstract class TlsProtocol
protected abstract TlsPeer getPeer();
- protected abstract void handleChangeCipherSpecMessage()
- throws IOException;
+ protected void handleChangeCipherSpecMessage() throws IOException
+ {
+ }
protected abstract void handleHandshakeMessage(short type, byte[] buf)
throws IOException;
@@ -93,37 +105,88 @@ public abstract class TlsProtocol
protected void handleWarningMessage(short description)
throws IOException
{
+ }
+
+ protected void cleanupHandshake()
+ {
+ if (this.expected_verify_data != null)
+ {
+ Arrays.fill(this.expected_verify_data, (byte)0);
+ this.expected_verify_data = null;
+ }
+
+ this.securityParameters.clear();
+ this.peerCertificate = null;
+ this.offeredCipherSuites = null;
+ this.offeredCompressionMethods = null;
+ this.clientExtensions = null;
+ this.serverExtensions = null;
+
+ this.resumedSession = false;
+ this.receivedChangeCipherSpec = false;
+ this.secure_renegotiation = false;
+ this.allowCertificateStatus = false;
+ this.expectSessionTicket = false;
}
protected void completeHandshake()
throws IOException
{
+ try
+ {
+ /*
+ * We will now read data, until we have completed the handshake.
+ */
+ while (this.connection_state != CS_END)
+ {
+ if (this.closed)
+ {
+ // TODO What kind of exception/alert?
+ }
- this.expected_verify_data = null;
+ safeReadRecord();
+ }
- /*
- * We will now read data, until we have completed the handshake.
- */
- while (this.connection_state != CS_SERVER_FINISHED)
- {
- safeReadRecord();
- }
+ this.recordStream.finaliseHandshake();
- this.recordStream.finaliseHandshake();
+ this.splitApplicationDataRecords = !TlsUtils.isTLSv11(getContext());
- ProtocolVersion version = getContext().getServerVersion();
- this.writeExtraEmptyRecords = version.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10);
+ /*
+ * If this was an initial handshake, we are now ready to send and receive application data.
+ */
+ if (!appDataReady)
+ {
+ this.appDataReady = true;
- /*
- * If this was an initial handshake, we are now ready to send and receive application data.
- */
- if (!appDataReady)
- {
- this.appDataReady = true;
+ this.tlsInputStream = new TlsInputStream(this);
+ this.tlsOutputStream = new TlsOutputStream(this);
+ }
- this.tlsInputStream = new TlsInputStream(this);
- this.tlsOutputStream = new TlsOutputStream(this);
+ if (this.tlsSession != null)
+ {
+ if (this.sessionParameters == null)
+ {
+ this.sessionParameters = new SessionParameters.Builder()
+ .setCipherSuite(this.securityParameters.cipherSuite)
+ .setCompressionAlgorithm(this.securityParameters.compressionAlgorithm)
+ .setMasterSecret(this.securityParameters.masterSecret)
+ .setPeerCertificate(this.peerCertificate)
+ // TODO Consider filtering extensions that aren't relevant to resumed sessions
+ .setServerExtensions(this.serverExtensions)
+ .build();
+
+ this.tlsSession = new TlsSessionImpl(this.tlsSession.getSessionID(), this.sessionParameters);
+ }
+
+ getContext().setResumableSession(this.tlsSession);
+ }
+
+ getPeer().notifyHandshakeComplete();
+ }
+ finally
+ {
+ cleanupHandshake();
}
}
@@ -135,26 +198,37 @@ public abstract class TlsProtocol
*/
switch (protocol)
{
- case ContentType.change_cipher_spec:
- changeCipherSpecQueue.addData(buf, offset, len);
- processChangeCipherSpec();
- break;
case ContentType.alert:
+ {
alertQueue.addData(buf, offset, len);
processAlert();
break;
- case ContentType.handshake:
- handshakeQueue.addData(buf, offset, len);
- processHandshake();
- break;
+ }
case ContentType.application_data:
+ {
if (!appDataReady)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
applicationDataQueue.addData(buf, offset, len);
processApplicationData();
break;
+ }
+ case ContentType.change_cipher_spec:
+ {
+ processChangeCipherSpec(buf, offset, len);
+ break;
+ }
+ case ContentType.handshake:
+ {
+ handshakeQueue.addData(buf, offset, len);
+ processHandshake();
+ break;
+ }
+ case ContentType.heartbeat:
+ {
+ // TODO[RFC 6520]
+ }
default:
/*
* Uh, we don't know this protocol.
@@ -190,9 +264,7 @@ public abstract class TlsProtocol
/*
* Read the message.
*/
- byte[] buf = new byte[len];
- handshakeQueue.read(buf, 0, len, 4);
- handshakeQueue.removeData(len + 4);
+ byte[] buf = handshakeQueue.removeData(len, 4);
/*
* RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
@@ -205,7 +277,6 @@ public abstract class TlsProtocol
break;
case HandshakeType.finished:
{
-
if (this.expected_verify_data == null)
{
this.expected_verify_data = createVerifyData(!getContext().isServer());
@@ -247,9 +318,7 @@ public abstract class TlsProtocol
/*
* An alert is always 2 bytes. Read the alert.
*/
- byte[] tmp = new byte[2];
- alertQueue.read(tmp, 0, 2, 0);
- alertQueue.removeData(2);
+ byte[] tmp = alertQueue.removeData(2, 0);
short level = tmp[0];
short description = tmp[1];
@@ -257,20 +326,17 @@ public abstract class TlsProtocol
if (level == AlertLevel.fatal)
{
+ /*
+ * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+ * without proper close_notify messages with level equal to warning.
+ */
+ invalidateSession();
this.failedWithError = true;
this.closed = true;
- /*
- * Now try to close the stream, ignore errors.
- */
- try
- {
- recordStream.close();
- }
- catch (Exception e)
- {
- }
+ recordStream.safeClose();
+
throw new IOException(TLS_ERROR_MESSAGE);
}
else
@@ -300,27 +366,27 @@ public abstract class TlsProtocol
* @throws IOException If the message has an invalid content or the handshake is not in the correct
* state.
*/
- private void processChangeCipherSpec()
+ private void processChangeCipherSpec(byte[] buf, int off, int len)
throws IOException
{
- while (changeCipherSpecQueue.size() > 0)
+ for (int i = 0; i < len; ++i)
{
- /*
- * A change cipher spec message is only one byte with the value 1.
- */
- byte[] b = new byte[1];
- changeCipherSpecQueue.read(b, 0, 1, 0);
- changeCipherSpecQueue.removeData(1);
- if (b[0] != 1)
+ short message = TlsUtils.readUint8(buf, off + i);
+
+ if (message != ChangeCipherSpec.change_cipher_spec)
{
- /*
- * This should never happen.
- */
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.decode_error);
}
- recordStream.receivedReadCipherSpec();
+ if (this.receivedChangeCipherSpec)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ this.receivedChangeCipherSpec = true;
+
+ recordStream.receivedReadCipherSpec();
+
handleChangeCipherSpecMessage();
}
}
@@ -338,7 +404,6 @@ public abstract class TlsProtocol
protected int readApplicationData(byte[] buf, int offset, int len)
throws IOException
{
-
if (len < 1)
{
return 0;
@@ -367,9 +432,9 @@ public abstract class TlsProtocol
safeReadRecord();
}
+
len = Math.min(len, applicationDataQueue.size());
- applicationDataQueue.read(buf, offset, len, 0);
- applicationDataQueue.removeData(len);
+ applicationDataQueue.removeData(buf, offset, len, 0);
return len;
}
@@ -378,13 +443,18 @@ public abstract class TlsProtocol
{
try
{
- recordStream.readRecord();
+ if (!recordStream.readRecord())
+ {
+ // TODO It would be nicer to allow graceful connection close if between records
+// this.failWithError(AlertLevel.warning, AlertDescription.close_notify);
+ throw new EOFException();
+ }
}
catch (TlsFatalAlert e)
{
if (!this.closed)
{
- this.failWithError(AlertLevel.fatal, e.getAlertDescription());
+ this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to read record", e);
}
throw e;
}
@@ -392,7 +462,7 @@ public abstract class TlsProtocol
{
if (!this.closed)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
}
throw e;
}
@@ -400,7 +470,7 @@ public abstract class TlsProtocol
{
if (!this.closed)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
}
throw e;
}
@@ -417,7 +487,7 @@ public abstract class TlsProtocol
{
if (!this.closed)
{
- this.failWithError(AlertLevel.fatal, e.getAlertDescription());
+ this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to write record", e);
}
throw e;
}
@@ -425,7 +495,7 @@ public abstract class TlsProtocol
{
if (!closed)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
}
throw e;
}
@@ -433,7 +503,7 @@ public abstract class TlsProtocol
{
if (!closed)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
}
throw e;
}
@@ -467,25 +537,41 @@ public abstract class TlsProtocol
/*
* RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are
* potentially useful as a traffic analysis countermeasure.
+ *
+ * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting.
*/
- if (this.writeExtraEmptyRecords)
+
+ if (this.splitApplicationDataRecords)
{
/*
* Protect against known IV attack!
*
- * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
+ * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
*/
- safeWriteRecord(ContentType.application_data, TlsUtils.EMPTY_BYTES, 0, 0);
+ safeWriteRecord(ContentType.application_data, buf, offset, 1);
+ ++offset;
+ --len;
}
- /*
- * We are only allowed to write fragments up to 2^14 bytes.
- */
- int toWrite = Math.min(len, 1 << 14);
-
- safeWriteRecord(ContentType.application_data, buf, offset, toWrite);
+ if (len > 0)
+ {
+ // Fragment data according to the current fragment limit.
+ int toWrite = Math.min(len, recordStream.getPlaintextLimit());
+ safeWriteRecord(ContentType.application_data, buf, offset, toWrite);
+ offset += toWrite;
+ len -= toWrite;
+ }
+ }
+ }
- offset += toWrite;
+ protected void writeHandshakeMessage(byte[] buf, int off, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ // Fragment data according to the current fragment limit.
+ int toWrite = Math.min(len, recordStream.getPlaintextLimit());
+ safeWriteRecord(ContentType.handshake, buf, off, toWrite);
+ off += toWrite;
len -= toWrite;
}
}
@@ -507,15 +593,16 @@ public abstract class TlsProtocol
}
/**
- * Terminate this connection with an alert.
- * <p/>
- * Can be used for normal closure too.
- *
- * @param alertLevel The level of the alert, an be AlertLevel.fatal or AL_warning.
- * @param alertDescription The exact alert message.
- * @throws IOException If alert was fatal.
+ * Terminate this connection with an alert. Can be used for normal closure too.
+ *
+ * @param alertLevel
+ * See {@link AlertLevel} for values.
+ * @param alertDescription
+ * See {@link AlertDescription} for values.
+ * @throws IOException
+ * If alert was fatal.
*/
- protected void failWithError(short alertLevel, short alertDescription)
+ protected void failWithError(short alertLevel, short alertDescription, String message, Exception cause)
throws IOException
{
/*
@@ -531,27 +618,43 @@ public abstract class TlsProtocol
if (alertLevel == AlertLevel.fatal)
{
/*
- * This is a fatal message.
+ * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+ * without proper close_notify messages with level equal to warning.
*/
+ // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete.
+ invalidateSession();
+
this.failedWithError = true;
}
- raiseAlert(alertLevel, alertDescription, null, null);
- recordStream.close();
- if (alertLevel == AlertLevel.fatal)
+ raiseAlert(alertLevel, alertDescription, message, cause);
+ recordStream.safeClose();
+ if (alertLevel != AlertLevel.fatal)
{
- throw new IOException(TLS_ERROR_MESSAGE);
+ return;
}
}
- else
+
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+
+ protected void invalidateSession()
+ {
+ if (this.sessionParameters != null)
{
- throw new IOException(TLS_ERROR_MESSAGE);
+ this.sessionParameters.clear();
+ this.sessionParameters = null;
+ }
+
+ if (this.tlsSession != null)
+ {
+ this.tlsSession.invalidate();
+ this.tlsSession = null;
}
}
protected void processFinishedMessage(ByteArrayInputStream buf)
throws IOException
{
-
byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf);
assertEmpty(buf);
@@ -564,14 +667,13 @@ public abstract class TlsProtocol
/*
* Wrong checksum in the finished message.
*/
- this.failWithError(AlertLevel.fatal, AlertDescription.decrypt_error);
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
}
}
protected void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause)
throws IOException
{
-
getPeer().notifyAlertRaised(alertLevel, alertDescription, message, cause);
byte[] error = new byte[2];
@@ -590,7 +692,6 @@ public abstract class TlsProtocol
protected void sendCertificateMessage(Certificate certificate)
throws IOException
{
-
if (certificate == null)
{
certificate = Certificate.EMPTY_CHAIN;
@@ -611,25 +712,17 @@ public abstract class TlsProtocol
}
}
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- TlsUtils.writeUint8(HandshakeType.certificate, bos);
-
- // Reserve space for length
- TlsUtils.writeUint24(0, bos);
-
- certificate.encode(bos);
- byte[] message = bos.toByteArray();
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate);
- // Patch actual length back in
- TlsUtils.writeUint24(message.length - 4, message, 1);
+ certificate.encode(message);
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
}
protected void sendChangeCipherSpecMessage()
throws IOException
{
- byte[] message = new byte[]{1};
+ byte[] message = new byte[]{ 1 };
safeWriteRecord(ContentType.change_cipher_spec, message, 0, message.length);
recordStream.sentWriteCipherSpec();
}
@@ -639,33 +732,21 @@ public abstract class TlsProtocol
{
byte[] verify_data = createVerifyData(getContext().isServer());
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- TlsUtils.writeUint8(HandshakeType.finished, bos);
- TlsUtils.writeUint24(verify_data.length, bos);
- bos.write(verify_data);
- byte[] message = bos.toByteArray();
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.length);
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.write(verify_data);
+
+ message.writeToRecordStream();
}
protected void sendSupplementalDataMessage(Vector supplementalData)
throws IOException
{
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data);
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeUint8(HandshakeType.supplemental_data, buf);
-
- // Reserve space for length
- TlsUtils.writeUint24(0, buf);
+ writeSupplementalData(message, supplementalData);
- writeSupplementalData(buf, supplementalData);
-
- byte[] message = buf.toByteArray();
-
- // Patch actual length back in
- TlsUtils.writeUint24(message.length - 4, message, 1);
-
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
}
protected byte[] createVerifyData(boolean isServer)
@@ -674,12 +755,12 @@ public abstract class TlsProtocol
if (isServer)
{
- return TlsUtils.calculateVerifyData(context, "server finished",
- recordStream.getCurrentHash(TlsUtils.SSL_SERVER));
+ return TlsUtils.calculateVerifyData(context, ExporterLabel.server_finished,
+ getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_SERVER));
}
- return TlsUtils.calculateVerifyData(context, "client finished",
- recordStream.getCurrentHash(TlsUtils.SSL_CLIENT));
+ return TlsUtils.calculateVerifyData(context, ExporterLabel.client_finished,
+ getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_CLIENT));
}
/**
@@ -702,7 +783,7 @@ public abstract class TlsProtocol
{
raiseWarning(AlertDescription.user_canceled, "User canceled handshake");
}
- this.failWithError(AlertLevel.warning, AlertDescription.close_notify);
+ this.failWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null);
}
}
@@ -712,28 +793,18 @@ public abstract class TlsProtocol
recordStream.flush();
}
- protected static boolean arrayContains(short[] a, short n)
- {
- for (int i = 0; i < a.length; ++i)
- {
- if (a[i] == n)
- {
- return true;
- }
- }
- return false;
- }
-
- protected static boolean arrayContains(int[] a, int n)
+ protected short processMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions, short alertDescription)
+ throws IOException
{
- for (int i = 0; i < a.length; ++i)
+ short maxFragmentLength = TlsExtensionsUtils.getMaxFragmentLengthExtension(serverExtensions);
+ if (maxFragmentLength >= 0 && !this.resumedSession)
{
- if (a[i] == n)
+ if (maxFragmentLength != TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions))
{
- return true;
+ throw new TlsFatalAlert(alertDescription);
}
}
- return false;
+ return maxFragmentLength;
}
/**
@@ -753,25 +824,28 @@ public abstract class TlsProtocol
protected static byte[] createRandomBlock(SecureRandom random)
{
+ random.setSeed(System.currentTimeMillis());
+
byte[] result = new byte[32];
random.nextBytes(result);
- TlsUtils.writeGMTUnixTime(result, 0);
+ /*
+ * The consensus seems to be that using the time here is neither all that useful, nor
+ * secure. Perhaps there could be an option to (re-)enable it. Instead, we seed the random
+ * source with the current time to retain it's main benefit.
+ */
+// TlsUtils.writeGMTUnixTime(result, 0);
return result;
}
protected static byte[] createRenegotiationInfo(byte[] renegotiated_connection)
throws IOException
{
-
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeOpaque8(renegotiated_connection, buf);
- return buf.toByteArray();
+ return TlsUtils.encodeOpaque8(renegotiated_connection);
}
protected static void establishMasterSecret(TlsContext context, TlsKeyExchange keyExchange)
throws IOException
{
-
byte[] pre_master_secret = keyExchange.generatePremasterSecret();
try
@@ -792,10 +866,26 @@ public abstract class TlsProtocol
}
}
+ /**
+ * 'sender' only relevant to SSLv3
+ */
+ protected static byte[] getCurrentPRFHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender)
+ {
+ Digest d = handshakeHash.forkPRFHash();
+
+ if (sslSender != null && TlsUtils.isSSL(context))
+ {
+ d.update(sslSender, 0, sslSender.length);
+ }
+
+ byte[] bs = new byte[d.getDigestSize()];
+ d.doFinal(bs, 0);
+ return bs;
+ }
+
protected static Hashtable readExtensions(ByteArrayInputStream input)
throws IOException
{
-
if (input.available() < 1)
{
return null;
@@ -812,13 +902,13 @@ public abstract class TlsProtocol
while (buf.available() > 0)
{
- Integer extType = Integers.valueOf(TlsUtils.readUint16(buf));
- byte[] extValue = TlsUtils.readOpaque16(buf);
+ Integer extension_type = Integers.valueOf(TlsUtils.readUint16(buf));
+ byte[] extension_data = TlsUtils.readOpaque16(buf);
/*
* RFC 3546 2.3 There MUST NOT be more than one extension of the same type.
*/
- if (null != extensions.put(extType, extValue))
+ if (null != extensions.put(extension_type, extension_data))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
@@ -830,7 +920,6 @@ public abstract class TlsProtocol
protected static Vector readSupplementalDataMessage(ByteArrayInputStream input)
throws IOException
{
-
byte[] supp_data = TlsUtils.readOpaque24(input);
assertEmpty(input);
@@ -853,17 +942,18 @@ public abstract class TlsProtocol
protected static void writeExtensions(OutputStream output, Hashtable extensions)
throws IOException
{
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
Enumeration keys = extensions.keys();
while (keys.hasMoreElements())
{
- Integer extType = (Integer)keys.nextElement();
- byte[] extValue = (byte[])extensions.get(extType);
+ Integer key = (Integer)keys.nextElement();
+ int extension_type = key.intValue();
+ byte[] extension_data = (byte[])extensions.get(key);
- TlsUtils.writeUint16(extType.intValue(), buf);
- TlsUtils.writeOpaque16(extValue, buf);
+ TlsUtils.checkUint16(extension_type);
+ TlsUtils.writeUint16(extension_type, buf);
+ TlsUtils.writeOpaque16(extension_data, buf);
}
byte[] extBytes = buf.toByteArray();
@@ -874,14 +964,15 @@ public abstract class TlsProtocol
protected static void writeSupplementalData(OutputStream output, Vector supplementalData)
throws IOException
{
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
for (int i = 0; i < supplementalData.size(); ++i)
{
SupplementalDataEntry entry = (SupplementalDataEntry)supplementalData.elementAt(i);
- TlsUtils.writeUint16(entry.getDataType(), buf);
+ int supp_data_type = entry.getDataType();
+ TlsUtils.checkUint16(supp_data_type);
+ TlsUtils.writeUint16(supp_data_type, buf);
TlsUtils.writeOpaque16(entry.getData(), buf);
}
@@ -890,54 +981,137 @@ public abstract class TlsProtocol
TlsUtils.writeOpaque24(supp_data, output);
}
- protected static int getPRFAlgorithm(int ciphersuite)
+ protected static int getPRFAlgorithm(TlsContext context, int ciphersuite) throws IOException
{
+ boolean isTLSv12 = TlsUtils.isTLSv12(context);
switch (ciphersuite)
{
case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
- case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
- case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
- case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
- case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
- return PRFAlgorithm.tls_prf_sha256;
+ {
+ if (isTLSv12)
+ {
+ return PRFAlgorithm.tls_prf_sha256;
+ }
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
- return PRFAlgorithm.tls_prf_sha384;
+ {
+ if (isTLSv12)
+ {
+ return PRFAlgorithm.tls_prf_sha384;
+ }
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+ {
+ if (isTLSv12)
+ {
+ return PRFAlgorithm.tls_prf_sha384;
+ }
+ return PRFAlgorithm.tls_prf_legacy;
+ }
default:
+ {
+ if (isTLSv12)
+ {
+ return PRFAlgorithm.tls_prf_sha256;
+ }
return PRFAlgorithm.tls_prf_legacy;
}
+ }
+ }
+
+ class HandshakeMessage extends ByteArrayOutputStream
+ {
+ HandshakeMessage(short handshakeType) throws IOException
+ {
+ this(handshakeType, 60);
+ }
+
+ HandshakeMessage(short handshakeType, int length) throws IOException
+ {
+ super(length + 4);
+ TlsUtils.writeUint8(handshakeType, this);
+ // Reserve space for length
+ count += 3;
+ }
+
+ void writeToRecordStream() throws IOException
+ {
+ // Patch actual length back in
+ int length = count - 4;
+ TlsUtils.checkUint24(length);
+ TlsUtils.writeUint24(length, buf, 1);
+ writeHandshakeMessage(buf, 0, count);
+ buf = null;
+ }
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
index 24eec53..8970968 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
@@ -40,7 +40,6 @@ public class TlsRSAKeyExchange
public void processServerCredentials(TlsCredentials serverCredentials)
throws IOException
{
-
if (!(serverCredentials instanceof TlsEncryptionCredentials))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -54,7 +53,6 @@ public class TlsRSAKeyExchange
public void processServerCertificate(Certificate serverCertificate)
throws IOException
{
-
if (serverCertificate.isEmpty())
{
throw new TlsFatalAlert(AlertDescription.bad_certificate);
@@ -115,15 +113,14 @@ public class TlsRSAKeyExchange
public void generateClientKeyExchange(OutputStream output)
throws IOException
{
- this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, output);
+ this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, rsaServerPublicKey, output);
}
public void processClientKeyExchange(InputStream input)
throws IOException
{
-
byte[] encryptedPreMasterSecret;
- if (context.getServerVersion().isSSL())
+ if (TlsUtils.isSSL(context))
{
// TODO Do any SSLv3 clients actually include the length?
encryptedPreMasterSecret = Streams.readAll(input);
@@ -133,68 +130,7 @@ public class TlsRSAKeyExchange
encryptedPreMasterSecret = TlsUtils.readOpaque16(input);
}
- ProtocolVersion clientVersion = context.getClientVersion();
-
- /*
- * RFC 5246 7.4.7.1.
- */
- {
- // TODO Provide as configuration option?
- boolean versionNumberCheckDisabled = false;
-
- /*
- * See notes regarding Bleichenbacher/Klima attack. The code here implements the first
- * construction proposed there, which is RECOMMENDED.
- */
- byte[] R = new byte[48];
- this.context.getSecureRandom().nextBytes(R);
-
- byte[] M = TlsUtils.EMPTY_BYTES;
- try
- {
- M = serverCredentials.decryptPreMasterSecret(encryptedPreMasterSecret);
- }
- catch (Exception e)
- {
- /*
- * In any case, a TLS server MUST NOT generate an alert if processing an
- * RSA-encrypted premaster secret message fails, or the version number is not as
- * expected. Instead, it MUST continue the handshake with a randomly generated
- * premaster secret.
- */
- }
-
- if (M.length != 48)
- {
- TlsUtils.writeVersion(clientVersion, R, 0);
- this.premasterSecret = R;
- }
- else
- {
- /*
- * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST
- * check the version number [..].
- */
- if (versionNumberCheckDisabled && clientVersion.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10))
- {
- /*
- * If the version number is TLS 1.0 or earlier, server implementations SHOULD
- * check the version number, but MAY have a configuration option to disable the
- * check.
- */
- }
- else
- {
- /*
- * Note that explicitly constructing the pre_master_secret with the
- * ClientHello.client_version produces an invalid master_secret if the client
- * has sent the wrong version in the original pre_master_secret.
- */
- TlsUtils.writeVersion(clientVersion, M, 0);
- }
- this.premasterSecret = M;
- }
- }
+ this.premasterSecret = TlsRSAUtils.safeDecryptPreMasterSecret(context, serverCredentials, encryptedPreMasterSecret);
}
public byte[] generatePremasterSecret()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java
index d9f7975..35538a7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java
@@ -5,6 +5,7 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.digests.NullDigest;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
@@ -12,40 +13,37 @@ import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.signers.GenericSigner;
import org.bouncycastle.crypto.signers.RSADigestSigner;
-import org.bouncycastle.util.Arrays;
public class TlsRSASigner
extends AbstractTlsSigner
{
-
- public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+ public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm,
+ AsymmetricKeyParameter privateKey, byte[] hash)
throws CryptoException
{
-
- AsymmetricBlockCipher engine = createRSAImpl();
- engine.init(true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
- return engine.processBlock(md5AndSha1, 0, md5AndSha1.length);
+ Signer signer = makeSigner(algorithm, true, true,
+ new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+ signer.update(hash, 0, hash.length);
+ return signer.generateSignature();
}
- public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+ public boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+ AsymmetricKeyParameter publicKey, byte[] hash)
throws CryptoException
{
-
- AsymmetricBlockCipher engine = createRSAImpl();
- engine.init(false, publicKey);
- byte[] signed = engine.processBlock(sigBytes, 0, sigBytes.length);
- return Arrays.constantTimeAreEqual(signed, md5AndSha1);
+ Signer signer = makeSigner(algorithm, true, false, publicKey);
+ signer.update(hash, 0, hash.length);
+ return signer.verifySignature(sigBytes);
}
- public Signer createSigner(AsymmetricKeyParameter privateKey)
+ public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey)
{
- return makeSigner(new CombinedHash(), true,
- new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+ return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
}
- public Signer createVerifyer(AsymmetricKeyParameter publicKey)
+ public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey)
{
- return makeSigner(new CombinedHash(), false, publicKey);
+ return makeSigner(algorithm, false, false, publicKey);
}
public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
@@ -53,16 +51,41 @@ public class TlsRSASigner
return publicKey instanceof RSAKeyParameters && !publicKey.isPrivate();
}
- protected Signer makeSigner(Digest d, boolean forSigning, CipherParameters cp)
+ protected Signer makeSigner(SignatureAndHashAlgorithm algorithm, boolean raw, boolean forSigning,
+ CipherParameters cp)
{
+ if ((algorithm != null) != TlsUtils.isTLSv12(context))
+ {
+ throw new IllegalStateException();
+ }
+
+ if (algorithm != null && algorithm.getSignature() != SignatureAlgorithm.rsa)
+ {
+ throw new IllegalStateException();
+ }
+
+ Digest d;
+ if (raw)
+ {
+ d = new NullDigest();
+ }
+ else if (algorithm == null)
+ {
+ d = new CombinedHash();
+ }
+ else
+ {
+ d = TlsUtils.createHash(algorithm.getHash());
+ }
+
Signer s;
- if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion()))
+ if (algorithm != null)
{
/*
* RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated
* using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1].
*/
- s = new RSADigestSigner(d);
+ s = new RSADigestSigner(d, TlsUtils.getOIDForHashAlgorithm(algorithm.getHash()));
}
else
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
index f67e572..e3856bd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
@@ -12,8 +12,7 @@ import org.bouncycastle.crypto.params.RSAKeyParameters;
public class TlsRSAUtils
{
public static byte[] generateEncryptedPreMasterSecret(TlsContext context, RSAKeyParameters rsaServerPublicKey,
- OutputStream output)
- throws IOException
+ OutputStream output) throws IOException
{
/*
* Choose a PremasterSecret and send it encrypted to the server
@@ -29,7 +28,7 @@ public class TlsRSAUtils
{
byte[] encryptedPreMasterSecret = encoding.processBlock(premasterSecret, 0, premasterSecret.length);
- if (context.getServerVersion().isSSL())
+ if (TlsUtils.isSSL(context))
{
// TODO Do any SSLv3 servers actually expect the length?
output.write(encryptedPreMasterSecret);
@@ -49,4 +48,69 @@ public class TlsRSAUtils
return premasterSecret;
}
+
+ public static byte[] safeDecryptPreMasterSecret(TlsContext context, TlsEncryptionCredentials encryptionCredentials,
+ byte[] encryptedPreMasterSecret)
+ {
+ /*
+ * RFC 5246 7.4.7.1.
+ */
+
+ ProtocolVersion clientVersion = context.getClientVersion();
+
+ // TODO Provide as configuration option?
+ boolean versionNumberCheckDisabled = false;
+
+ /*
+ * See notes regarding Bleichenbacher/Klima attack. The code here implements the first
+ * construction proposed there, which is RECOMMENDED.
+ */
+ byte[] R = new byte[48];
+ context.getSecureRandom().nextBytes(R);
+
+ byte[] M = TlsUtils.EMPTY_BYTES;
+ try
+ {
+ M = encryptionCredentials.decryptPreMasterSecret(encryptedPreMasterSecret);
+ }
+ catch (Exception e)
+ {
+ /*
+ * In any case, a TLS server MUST NOT generate an alert if processing an
+ * RSA-encrypted premaster secret message fails, or the version number is not as
+ * expected. Instead, it MUST continue the handshake with a randomly generated
+ * premaster secret.
+ */
+ }
+
+ if (M.length != 48)
+ {
+ TlsUtils.writeVersion(clientVersion, R, 0);
+ return R;
+ }
+
+ /*
+ * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST
+ * check the version number [..].
+ */
+ if (versionNumberCheckDisabled && clientVersion.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10))
+ {
+ /*
+ * If the version number is TLS 1.0 or earlier, server implementations SHOULD
+ * check the version number, but MAY have a configuration option to disable the
+ * check.
+ */
+ }
+ else
+ {
+ /*
+ * Note that explicitly constructing the pre_master_secret with the
+ * ClientHello.client_version produces an invalid master_secret if the client
+ * has sent the wrong version in the original pre_master_secret.
+ */
+ TlsUtils.writeVersion(clientVersion, M, 0);
+ }
+
+ return M;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
index b928b91..452fbf9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
@@ -13,18 +13,16 @@ import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.agreement.srp.SRP6Client;
import org.bouncycastle.crypto.agreement.srp.SRP6Util;
import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.io.SignerInputStream;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.io.TeeInputStream;
/**
* TLS 1.1 SRP key exchange (RFC 5054).
*/
-public class TlsSRPKeyExchange
- extends AbstractTlsKeyExchange
+public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
{
-
protected TlsSigner tlsSigner;
protected byte[] identity;
protected byte[] password;
@@ -37,7 +35,6 @@ public class TlsSRPKeyExchange
public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity, byte[] password)
{
-
super(keyExchange, supportedSignatureAlgorithms);
switch (keyExchange)
@@ -64,14 +61,12 @@ public class TlsSRPKeyExchange
{
super.init(context);
- if (this.tlsSigner != null)
- {
+ if (this.tlsSigner != null) {
this.tlsSigner.init(context);
}
}
- public void skipServerCredentials()
- throws IOException
+ public void skipServerCredentials() throws IOException
{
if (tlsSigner != null)
{
@@ -79,10 +74,8 @@ public class TlsSRPKeyExchange
}
}
- public void processServerCertificate(Certificate serverCertificate)
- throws IOException
+ public void processServerCertificate(Certificate serverCertificate) throws IOException
{
-
if (tlsSigner == null)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
@@ -119,31 +112,31 @@ public class TlsSRPKeyExchange
return true;
}
- public void processServerKeyExchange(InputStream input)
- throws IOException
+ public void processServerKeyExchange(InputStream input) throws IOException
{
-
SecurityParameters securityParameters = context.getSecurityParameters();
- InputStream sigIn = input;
- Signer signer = null;
+ SignerInputBuffer buf = null;
+ InputStream teeIn = input;
if (tlsSigner != null)
{
- signer = initVerifyer(tlsSigner, securityParameters);
- sigIn = new SignerInputStream(input, signer);
+ buf = new SignerInputBuffer();
+ teeIn = new TeeInputStream(input, buf);
}
- byte[] NBytes = TlsUtils.readOpaque16(sigIn);
- byte[] gBytes = TlsUtils.readOpaque16(sigIn);
- byte[] sBytes = TlsUtils.readOpaque8(sigIn);
- byte[] BBytes = TlsUtils.readOpaque16(sigIn);
+ byte[] NBytes = TlsUtils.readOpaque16(teeIn);
+ byte[] gBytes = TlsUtils.readOpaque16(teeIn);
+ byte[] sBytes = TlsUtils.readOpaque8(teeIn);
+ byte[] BBytes = TlsUtils.readOpaque16(teeIn);
- if (signer != null)
+ if (buf != null)
{
- byte[] sigByte = TlsUtils.readOpaque16(input);
+ DigitallySigned signed_params = DigitallySigned.parse(context, input);
- if (!signer.verifySignature(sigByte))
+ Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters);
+ buf.updateSigner(signer);
+ if (!signer.verifySignature(signed_params.getSignature()))
{
throw new TlsFatalAlert(AlertDescription.decrypt_error);
}
@@ -153,7 +146,7 @@ public class TlsSRPKeyExchange
BigInteger g = new BigInteger(1, gBytes);
// TODO Validate group parameters (see RFC 5054)
- // handler.failWithError(AlertLevel.fatal, AlertDescription.insufficient_security);
+// throw new TlsFatalAlert(AlertDescription.insufficient_security);
this.s = sBytes;
@@ -173,28 +166,23 @@ public class TlsSRPKeyExchange
this.srpClient.init(N, g, new SHA1Digest(), context.getSecureRandom());
}
- public void validateCertificateRequest(CertificateRequest certificateRequest)
- throws IOException
+ public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- public void processClientCredentials(TlsCredentials clientCredentials)
- throws IOException
+ public void processClientCredentials(TlsCredentials clientCredentials) throws IOException
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
- public void generateClientKeyExchange(OutputStream output)
- throws IOException
+ public void generateClientKeyExchange(OutputStream output) throws IOException
{
- byte[] keData = BigIntegers.asUnsignedByteArray(srpClient.generateClientCredentials(s, this.identity,
- this.password));
- TlsUtils.writeOpaque16(keData, output);
+ BigInteger A = srpClient.generateClientCredentials(s, this.identity, this.password);
+ TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(A), output);
}
- public byte[] generatePremasterSecret()
- throws IOException
+ public byte[] generatePremasterSecret() throws IOException
{
try
{
@@ -207,9 +195,9 @@ public class TlsSRPKeyExchange
}
}
- protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+ protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
{
- Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+ Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey);
signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
return signer;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java
new file mode 100644
index 0000000..7fe5fb8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java
@@ -0,0 +1,49 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.util.Integers;
+
+public class TlsSRPUtils
+{
+ public static final Integer EXT_SRP = Integers.valueOf(ExtensionType.srp);
+
+ public static void addSRPExtension(Hashtable extensions, byte[] identity) throws IOException
+ {
+ extensions.put(EXT_SRP, createSRPExtension(identity));
+ }
+
+ public static byte[] getSRPExtension(Hashtable extensions) throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_SRP);
+ return extensionData == null ? null : readSRPExtension(extensionData);
+ }
+
+ public static byte[] createSRPExtension(byte[] identity) throws IOException
+ {
+ if (identity == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return TlsUtils.encodeOpaque8(identity);
+ }
+
+ public static byte[] readSRPExtension(byte[] extensionData) throws IOException
+ {
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+ byte[] identity = TlsUtils.readOpaque8(buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ return identity;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java
index f82f94d..da98b7a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java
@@ -12,36 +12,24 @@ import org.bouncycastle.util.Integers;
*/
public class TlsSRTPUtils
{
-
public static final Integer EXT_use_srtp = Integers.valueOf(ExtensionType.use_srtp);
public static void addUseSRTPExtension(Hashtable extensions, UseSRTPData useSRTPData)
throws IOException
{
-
extensions.put(EXT_use_srtp, createUseSRTPExtension(useSRTPData));
}
public static UseSRTPData getUseSRTPExtension(Hashtable extensions)
throws IOException
{
-
- if (extensions == null)
- {
- return null;
- }
- byte[] extensionValue = (byte[])extensions.get(EXT_use_srtp);
- if (extensionValue == null)
- {
- return null;
- }
- return readUseSRTPExtension(extensionValue);
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_use_srtp);
+ return extensionData == null ? null : readUseSRTPExtension(extensionData);
}
public static byte[] createUseSRTPExtension(UseSRTPData useSRTPData)
throws IOException
{
-
if (useSRTPData == null)
{
throw new IllegalArgumentException("'useSRTPData' cannot be null");
@@ -50,9 +38,7 @@ public class TlsSRTPUtils
ByteArrayOutputStream buf = new ByteArrayOutputStream();
// SRTPProtectionProfiles
- int[] protectionProfiles = useSRTPData.getProtectionProfiles();
- TlsUtils.writeUint16(2 * protectionProfiles.length, buf);
- TlsUtils.writeUint16Array(protectionProfiles, buf);
+ TlsUtils.writeUint16ArrayWithUint16Length(useSRTPData.getProtectionProfiles(), buf);
// srtp_mki
TlsUtils.writeOpaque8(useSRTPData.getMki(), buf);
@@ -60,16 +46,15 @@ public class TlsSRTPUtils
return buf.toByteArray();
}
- public static UseSRTPData readUseSRTPExtension(byte[] extensionValue)
+ public static UseSRTPData readUseSRTPExtension(byte[] extensionData)
throws IOException
{
-
- if (extensionValue == null)
+ if (extensionData == null)
{
- throw new IllegalArgumentException("'extensionValue' cannot be null");
+ throw new IllegalArgumentException("'extensionData' cannot be null");
}
- ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
// SRTPProtectionProfiles
int length = TlsUtils.readUint16(buf);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
index 0b46391..85c0a9a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
@@ -7,11 +7,9 @@ import java.util.Vector;
public interface TlsServer
extends TlsPeer
{
-
void init(TlsServerContext context);
- void notifyClientVersion(ProtocolVersion clientVersion)
- throws IOException;
+ void notifyClientVersion(ProtocolVersion clientVersion) throws IOException;
void notifyOfferedCipherSuites(int[] offeredCipherSuites)
throws IOException;
@@ -19,9 +17,6 @@ public interface TlsServer
void notifyOfferedCompressionMethods(short[] offeredCompressionMethods)
throws IOException;
- void notifySecureRenegotiation(boolean secureNegotiation)
- throws IOException;
-
// Hashtable is (Integer -> byte[])
void processClientExtensions(Hashtable clientExtensions)
throws IOException;
@@ -46,32 +41,41 @@ public interface TlsServer
TlsCredentials getCredentials()
throws IOException;
+ /**
+ * This method will be called (only) if the server included an extension of type
+ * "status_request" with empty "extension_data" in the extended server hello. See <i>RFC 3546
+ * 3.6. Certificate Status Request</i>. If a non-null {@link CertificateStatus} is returned, it
+ * is sent to the client as a handshake message of type "certificate_status".
+ *
+ * @return A {@link CertificateStatus} to be sent to the client (or null for none).
+ * @throws IOException
+ */
+ CertificateStatus getCertificateStatus()
+ throws IOException;
+
TlsKeyExchange getKeyExchange()
throws IOException;
- CertificateRequest getCertificateRequest();
+ CertificateRequest getCertificateRequest()
+ throws IOException;
// Vector is (SupplementalDataEntry)
void processClientSupplementalData(Vector clientSupplementalData)
throws IOException;
/**
- * Called by the protocol handler to report the client certificate, only if a Certificate
- * {@link #getCertificateRequest()} returned non-null. Note: this method is responsible for
- * certificate verification and validation.
- *
- * @param clientCertificate the effective client certificate (may be an empty chain).
+ * Called by the protocol handler to report the client certificate, only if
+ * {@link #getCertificateRequest()} returned non-null.
+ *
+ * Note: this method is responsible for certificate verification and validation.
+ *
+ * @param clientCertificate
+ * the effective client certificate (may be an empty chain).
* @throws IOException
*/
void notifyClientCertificate(Certificate clientCertificate)
throws IOException;
- TlsCompression getCompression()
- throws IOException;
-
- TlsCipher getCipher()
- throws IOException;
-
/**
* RFC 5077 3.3. NewSessionTicket Handshake Message.
* <p/>
@@ -83,7 +87,4 @@ public interface TlsServer
*/
NewSessionTicket getNewSessionTicket()
throws IOException;
-
- void notifyHandshakeComplete()
- throws IOException;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java
index 2fa4029..48f028a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java
@@ -6,7 +6,6 @@ class TlsServerContextImpl
extends AbstractTlsContext
implements TlsServerContext
{
-
TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
{
super(secureRandom, securityParameters);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
index 961669f..056d22a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
@@ -1,12 +1,10 @@
package org.bouncycastle.crypto.tls;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
-import java.util.Hashtable;
import java.util.Vector;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -17,25 +15,15 @@ import org.bouncycastle.util.Arrays;
public class TlsServerProtocol
extends TlsProtocol
{
-
protected TlsServer tlsServer = null;
protected TlsServerContextImpl tlsServerContext = null;
- protected int[] offeredCipherSuites;
- protected short[] offeredCompressionMethods;
- protected Hashtable clientExtensions;
-
- protected int selectedCipherSuite;
- protected short selectedCompressionMethod;
- protected Hashtable serverExtensions;
-
protected TlsKeyExchange keyExchange = null;
protected TlsCredentials serverCredentials = null;
protected CertificateRequest certificateRequest = null;
protected short clientCertificateType = -1;
- protected Certificate clientCertificate = null;
- protected byte[] certificateVerifyHash = null;
+ protected TlsHandshakeHash prepareFinishHash = null;
public TlsServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
{
@@ -51,14 +39,13 @@ public class TlsServerProtocol
public void accept(TlsServer tlsServer)
throws IOException
{
-
if (tlsServer == null)
{
throw new IllegalArgumentException("'tlsServer' cannot be null");
}
if (this.tlsServer != null)
{
- throw new IllegalStateException("accept can only be called once");
+ throw new IllegalStateException("'accept' can only be called once");
}
this.tlsServer = tlsServer;
@@ -74,8 +61,16 @@ public class TlsServerProtocol
this.recordStream.setRestrictReadVersion(false);
completeHandshake();
+ }
- this.tlsServer.notifyHandshakeComplete();
+ protected void cleanupHandshake()
+ {
+ super.cleanupHandshake();
+
+ this.keyExchange = null;
+ this.serverCredentials = null;
+ this.certificateRequest = null;
+ this.prepareFinishHash = null;
}
protected AbstractTlsContext getContext()
@@ -88,36 +83,9 @@ public class TlsServerProtocol
return tlsServer;
}
- protected void handleChangeCipherSpecMessage()
- throws IOException
- {
-
- switch (this.connection_state)
- {
- case CS_CLIENT_KEY_EXCHANGE:
- {
- if (this.certificateVerifyHash != null)
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
- // NB: Fall through to next case label
- }
- case CS_CERTIFICATE_VERIFY:
- {
- this.connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC;
- break;
- }
- default:
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- }
- }
- }
-
protected void handleHandshakeMessage(short type, byte[] data)
throws IOException
{
-
ByteArrayInputStream buf = new ByteArrayInputStream(data);
switch (type)
@@ -134,21 +102,6 @@ public class TlsServerProtocol
sendServerHelloMessage();
this.connection_state = CS_SERVER_HELLO;
- // TODO This block could really be done before actually sending the hello
- {
- securityParameters.prfAlgorithm = getPRFAlgorithm(selectedCipherSuite);
- securityParameters.compressionAlgorithm = this.selectedCompressionMethod;
-
- /*
- * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
- * verify_data_length has a verify_data_length equal to 12. This includes all
- * existing cipher suites.
- */
- securityParameters.verifyDataLength = 12;
-
- recordStream.notifyHelloComplete();
- }
-
Vector serverSupplementalData = tlsServer.getServerSupplementalData();
if (serverSupplementalData != null)
{
@@ -160,6 +113,9 @@ public class TlsServerProtocol
this.keyExchange.init(getContext());
this.serverCredentials = tlsServer.getCredentials();
+
+ Certificate serverCertificate = null;
+
if (this.serverCredentials == null)
{
this.keyExchange.skipServerCredentials();
@@ -167,10 +123,29 @@ public class TlsServerProtocol
else
{
this.keyExchange.processServerCredentials(this.serverCredentials);
- sendCertificateMessage(this.serverCredentials.getCertificate());
+
+ serverCertificate = this.serverCredentials.getCertificate();
+ sendCertificateMessage(serverCertificate);
}
this.connection_state = CS_SERVER_CERTIFICATE;
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (serverCertificate == null || serverCertificate.isEmpty())
+ {
+ this.allowCertificateStatus = false;
+ }
+
+ if (this.allowCertificateStatus)
+ {
+ CertificateStatus certificateStatus = tlsServer.getCertificateStatus();
+ if (certificateStatus != null)
+ {
+ sendCertificateStatusMessage(certificateStatus);
+ }
+ }
+
+ this.connection_state = CS_CERTIFICATE_STATUS;
+
byte[] serverKeyExchange = this.keyExchange.generateServerKeyExchange();
if (serverKeyExchange != null)
{
@@ -184,7 +159,11 @@ public class TlsServerProtocol
if (this.certificateRequest != null)
{
this.keyExchange.validateCertificateRequest(certificateRequest);
+
sendCertificateRequestMessage(certificateRequest);
+
+ TlsUtils.trackHashAlgorithms(this.recordStream.getHandshakeHash(),
+ this.certificateRequest.getSupportedSignatureAlgorithms());
}
}
this.connection_state = CS_CERTIFICATE_REQUEST;
@@ -192,12 +171,12 @@ public class TlsServerProtocol
sendServerHelloDoneMessage();
this.connection_state = CS_SERVER_HELLO_DONE;
+ this.recordStream.getHandshakeHash().sealHashAlgorithms();
+
break;
}
default:
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
@@ -212,9 +191,7 @@ public class TlsServerProtocol
break;
}
default:
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
@@ -231,16 +208,14 @@ public class TlsServerProtocol
{
if (this.certificateRequest == null)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
receiveCertificateMessage(buf);
this.connection_state = CS_CLIENT_CERTIFICATE;
break;
}
default:
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
@@ -261,10 +236,7 @@ public class TlsServerProtocol
}
else
{
-
- ProtocolVersion equivalentTLSVersion = getContext().getServerVersion().getEquivalentTLSVersion();
-
- if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(equivalentTLSVersion))
+ if (TlsUtils.isTLSv12(getContext()))
{
/*
* RFC 5246 If no suitable certificate is available, the client MUST send a
@@ -272,13 +244,13 @@ public class TlsServerProtocol
*
* NOTE: In previous RFCs, this was SHOULD instead of MUST.
*/
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- else if (equivalentTLSVersion.isSSL())
+ else if (TlsUtils.isSSL(getContext()))
{
- if (clientCertificate == null)
+ if (this.peerCertificate == null)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
else
@@ -295,9 +267,7 @@ public class TlsServerProtocol
break;
}
default:
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
@@ -312,18 +282,18 @@ public class TlsServerProtocol
* signing capability (i.e., all certificates except those containing fixed
* Diffie-Hellman parameters).
*/
- if (this.certificateVerifyHash == null)
+ if (!expectCertificateVerifyMessage())
{
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
+
receiveCertificateVerifyMessage(buf);
this.connection_state = CS_CERTIFICATE_VERIFY;
+
break;
}
default:
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
@@ -331,24 +301,33 @@ public class TlsServerProtocol
{
switch (this.connection_state)
{
- case CS_CLIENT_CHANGE_CIPHER_SPEC:
+ case CS_CLIENT_KEY_EXCHANGE:
+ {
+ if (expectCertificateVerifyMessage())
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ // NB: Fall through to next case label
+ }
+ case CS_CERTIFICATE_VERIFY:
+ {
processFinishedMessage(buf);
this.connection_state = CS_CLIENT_FINISHED;
- if (expectSessionTicket)
+ if (this.expectSessionTicket)
{
sendNewSessionTicketMessage(tlsServer.getNewSessionTicket());
+ sendChangeCipherSpecMessage();
}
this.connection_state = CS_SERVER_SESSION_TICKET;
- sendChangeCipherSpecMessage();
- this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC;
-
sendFinishedMessage();
this.connection_state = CS_SERVER_FINISHED;
+ this.connection_state = CS_END;
break;
+ }
default:
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
break;
}
@@ -360,9 +339,7 @@ public class TlsServerProtocol
case HandshakeType.server_hello_done:
case HandshakeType.session_ticket:
default:
- // We do not support this!
- this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
}
@@ -377,7 +354,7 @@ public class TlsServerProtocol
* SSL 3.0 If the server has sent a certificate request Message, the client must send
* either the certificate message or a no_certificate alert.
*/
- if (getContext().getServerVersion().isSSL() && certificateRequest != null)
+ if (TlsUtils.isSSL(getContext()) && certificateRequest != null)
{
notifyClientCertificate(Certificate.EMPTY_CHAIN);
}
@@ -393,18 +370,17 @@ public class TlsServerProtocol
protected void notifyClientCertificate(Certificate clientCertificate)
throws IOException
{
-
if (certificateRequest == null)
{
throw new IllegalStateException();
}
- if (this.clientCertificate != null)
+ if (this.peerCertificate != null)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
- this.clientCertificate = clientCertificate;
+ this.peerCertificate = clientCertificate;
if (clientCertificate.isEmpty())
{
@@ -439,7 +415,6 @@ public class TlsServerProtocol
protected void receiveCertificateMessage(ByteArrayInputStream buf)
throws IOException
{
-
Certificate clientCertificate = Certificate.parse(buf);
assertEmpty(buf);
@@ -450,22 +425,24 @@ public class TlsServerProtocol
protected void receiveCertificateVerifyMessage(ByteArrayInputStream buf)
throws IOException
{
-
- byte[] clientCertificateSignature = TlsUtils.readOpaque16(buf);
+ DigitallySigned clientCertificateVerify = DigitallySigned.parse(getContext(), buf);
assertEmpty(buf);
// Verify the CertificateVerify message contains a correct signature.
try
{
- TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType);
- tlsSigner.init(getContext());
+ // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned
+ byte[] certificateVerifyHash = getCurrentPRFHash(getContext(), prepareFinishHash, null);
- org.bouncycastle.asn1.x509.Certificate x509Cert = this.clientCertificate.getCertificateAt(0);
+ org.bouncycastle.asn1.x509.Certificate x509Cert = this.peerCertificate.getCertificateAt(0);
SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
- tlsSigner.verifyRawSignature(clientCertificateSignature, publicKey, this.certificateVerifyHash);
+ TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType);
+ tlsSigner.init(getContext());
+ tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
+ clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash);
}
catch (Exception e)
{
@@ -476,42 +453,45 @@ public class TlsServerProtocol
protected void receiveClientHelloMessage(ByteArrayInputStream buf)
throws IOException
{
-
ProtocolVersion client_version = TlsUtils.readVersion(buf);
if (client_version.isDTLS())
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- /*
- * Read the client random
- */
byte[] client_random = TlsUtils.readFully(32, buf);
+ /*
+ * TODO RFC 5077 3.4. If a ticket is presented by the client, the server MUST NOT attempt to
+ * use the Session ID in the ClientHello for stateful session resumption.
+ */
byte[] sessionID = TlsUtils.readOpaque8(buf);
if (sessionID.length > 32)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
+ /*
+ * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session
+ * resumption request), this vector MUST include at least the cipher_suite from that
+ * session.
+ */
int cipher_suites_length = TlsUtils.readUint16(buf);
if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.decode_error);
+ throw new TlsFatalAlert(AlertDescription.decode_error);
}
+ this.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf);
/*
- * NOTE: "If the session_id field is not empty (implying a session resumption request) this
- * vector must include at least the cipher_suite from that session."
+ * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session
+ * resumption request), it MUST include the compression_method from that session.
*/
- this.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf);
-
int compression_methods_length = TlsUtils.readUint8(buf);
if (compression_methods_length < 1)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
-
this.offeredCompressionMethods = TlsUtils.readUint8Array(compression_methods_length, buf);
/*
@@ -545,7 +525,7 @@ public class TlsServerProtocol
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
* to TRUE.
*/
- if (arrayContains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+ if (Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
{
this.secure_renegotiation = true;
}
@@ -554,22 +534,19 @@ public class TlsServerProtocol
* The server MUST check if the "renegotiation_info" extension is included in the
* ClientHello.
*/
- if (clientExtensions != null)
+ byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo);
+ if (renegExtData != null)
{
- byte[] renegExtValue = (byte[])clientExtensions.get(EXT_RenegotiationInfo);
- if (renegExtValue != null)
+ /*
+ * If the extension is present, set secure_renegotiation flag to TRUE. The
+ * server MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake.
+ */
+ this.secure_renegotiation = true;
+
+ if (!Arrays.constantTimeAreEqual(renegExtData, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
{
- /*
- * If the extension is present, set secure_renegotiation flag to TRUE. The
- * server MUST then verify that the length of the "renegotiated_connection"
- * field is zero, and if it is not, MUST abort the handshake.
- */
- this.secure_renegotiation = true;
-
- if (!Arrays.constantTimeAreEqual(renegExtValue, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
- {
- this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- }
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
}
}
@@ -585,81 +562,65 @@ public class TlsServerProtocol
protected void receiveClientKeyExchangeMessage(ByteArrayInputStream buf)
throws IOException
{
-
this.keyExchange.processClientKeyExchange(buf);
assertEmpty(buf);
establishMasterSecret(getContext(), keyExchange);
+ recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
- /*
- * Initialize our cipher suite
- */
- recordStream.setPendingConnectionState(tlsServer.getCompression(), tlsServer.getCipher());
+ this.prepareFinishHash = recordStream.prepareToFinish();
- if (expectCertificateVerifyMessage())
+ if (!expectSessionTicket)
{
- this.certificateVerifyHash = recordStream.getCurrentHash(null);
+ sendChangeCipherSpecMessage();
}
}
protected void sendCertificateRequestMessage(CertificateRequest certificateRequest)
throws IOException
{
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_request);
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeUint8(HandshakeType.certificate_request, buf);
+ certificateRequest.encode(message);
- // Reserve space for length
- TlsUtils.writeUint24(0, buf);
+ message.writeToRecordStream();
+ }
- certificateRequest.encode(buf);
- byte[] message = buf.toByteArray();
+ protected void sendCertificateStatusMessage(CertificateStatus certificateStatus)
+ throws IOException
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_status);
- // Patch actual length back in
- TlsUtils.writeUint24(message.length - 4, message, 1);
+ certificateStatus.encode(message);
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
}
protected void sendNewSessionTicketMessage(NewSessionTicket newSessionTicket)
throws IOException
{
-
if (newSessionTicket == null)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeUint8(HandshakeType.session_ticket, buf);
-
- // Reserve space for length
- TlsUtils.writeUint24(0, buf);
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.session_ticket);
- newSessionTicket.encode(buf);
- byte[] message = buf.toByteArray();
+ newSessionTicket.encode(message);
- // Patch actual length back in
- TlsUtils.writeUint24(message.length - 4, message, 1);
-
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
}
protected void sendServerHelloMessage()
throws IOException
{
-
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- TlsUtils.writeUint8(HandshakeType.server_hello, buf);
-
- // Reserve space for length
- TlsUtils.writeUint24(0, buf);
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello);
ProtocolVersion server_version = tlsServer.getServerVersion();
if (!server_version.isEqualOrEarlierVersionOf(getContext().getClientVersion()))
{
- this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
recordStream.setReadVersion(server_version);
@@ -667,32 +628,34 @@ public class TlsServerProtocol
recordStream.setRestrictReadVersion(true);
getContext().setServerVersion(server_version);
- TlsUtils.writeVersion(server_version, buf);
+ TlsUtils.writeVersion(server_version, message);
- buf.write(this.securityParameters.serverRandom);
+ message.write(this.securityParameters.serverRandom);
/*
* The server may return an empty session_id to indicate that the session will not be cached
* and therefore cannot be resumed.
*/
- TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+ TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, message);
- this.selectedCipherSuite = tlsServer.getSelectedCipherSuite();
- if (!arrayContains(this.offeredCipherSuites, this.selectedCipherSuite)
- || this.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
- || this.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ int selectedCipherSuite = tlsServer.getSelectedCipherSuite();
+ if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite)
+ || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
{
- this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
+ securityParameters.cipherSuite = selectedCipherSuite;
- this.selectedCompressionMethod = tlsServer.getSelectedCompressionMethod();
- if (!arrayContains(this.offeredCompressionMethods, this.selectedCompressionMethod))
+ short selectedCompressionMethod = tlsServer.getSelectedCompressionMethod();
+ if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod))
{
- this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
+ securityParameters.compressionAlgorithm = selectedCompressionMethod;
- TlsUtils.writeUint16(this.selectedCipherSuite, buf);
- TlsUtils.writeUint8(this.selectedCompressionMethod, buf);
+ TlsUtils.writeUint16(selectedCipherSuite, message);
+ TlsUtils.writeUint8(selectedCompressionMethod, message);
this.serverExtensions = tlsServer.getServerExtensions();
@@ -701,9 +664,8 @@ public class TlsServerProtocol
*/
if (this.secure_renegotiation)
{
-
- boolean noRenegExt = this.serverExtensions == null
- || !this.serverExtensions.containsKey(EXT_RenegotiationInfo);
+ byte[] renegExtData = TlsUtils.getExtensionData(this.serverExtensions, EXT_RenegotiationInfo);
+ boolean noRenegExt = (null == renegExtData);
if (noRenegExt)
{
@@ -714,55 +676,81 @@ public class TlsServerProtocol
* because the client is signaling its willingness to receive the extension via the
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
*/
- if (this.serverExtensions == null)
- {
- this.serverExtensions = new Hashtable();
- }
/*
* If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
* "renegotiation_info" extension in the ServerHello message.
*/
+ this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.serverExtensions);
this.serverExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
}
}
+ /*
+ * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and send a server hello containing no
+ * extensions.
+ */
+
if (this.serverExtensions != null)
{
- this.expectSessionTicket = serverExtensions.containsKey(EXT_SessionTicket);
- writeExtensions(buf, this.serverExtensions);
+ this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(clientExtensions,
+ this.serverExtensions, AlertDescription.internal_error);
+
+ this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(this.serverExtensions);
+
+ /*
+ * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
+ * a session resumption handshake.
+ */
+ this.allowCertificateStatus = !this.resumedSession
+ && TlsUtils.hasExpectedEmptyExtensionData(this.serverExtensions, TlsExtensionsUtils.EXT_status_request,
+ AlertDescription.internal_error);
+
+ this.expectSessionTicket = !this.resumedSession
+ && TlsUtils.hasExpectedEmptyExtensionData(this.serverExtensions, TlsProtocol.EXT_SessionTicket,
+ AlertDescription.internal_error);
+
+ writeExtensions(message, this.serverExtensions);
+ }
+
+ if (this.securityParameters.maxFragmentLength >= 0)
+ {
+ int plainTextLimit = 1 << (8 + this.securityParameters.maxFragmentLength);
+ recordStream.setPlaintextLimit(plainTextLimit);
}
- byte[] message = buf.toByteArray();
+ securityParameters.prfAlgorithm = getPRFAlgorithm(getContext(), securityParameters.getCipherSuite());
- // Patch actual length back in
- TlsUtils.writeUint24(message.length - 4, message, 1);
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has
+ * a verify_data_length equal to 12. This includes all existing cipher suites.
+ */
+ securityParameters.verifyDataLength = 12;
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
+
+ this.recordStream.notifyHelloComplete();
}
protected void sendServerHelloDoneMessage()
throws IOException
{
-
byte[] message = new byte[4];
TlsUtils.writeUint8(HandshakeType.server_hello_done, message, 0);
TlsUtils.writeUint24(0, message, 1);
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ writeHandshakeMessage(message, 0, message.length);
}
protected void sendServerKeyExchangeMessage(byte[] serverKeyExchange)
throws IOException
{
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.server_key_exchange, serverKeyExchange.length);
- TlsUtils.writeUint8(HandshakeType.server_key_exchange, bos);
- TlsUtils.writeUint24(serverKeyExchange.length, bos);
- bos.write(serverKeyExchange);
- byte[] message = bos.toByteArray();
+ message.write(serverKeyExchange);
- safeWriteRecord(ContentType.handshake, message, 0, message.length);
+ message.writeToRecordStream();
}
protected boolean expectCertificateVerifyMessage()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java
new file mode 100644
index 0000000..9b5ad46
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsSession
+{
+ SessionParameters exportSessionParameters();
+
+ byte[] getSessionID();
+
+ void invalidate();
+
+ boolean isResumable();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java
new file mode 100644
index 0000000..615c442
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.util.Arrays;
+
+class TlsSessionImpl implements TlsSession
+{
+ final byte[] sessionID;
+ SessionParameters sessionParameters;
+
+ TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters)
+ {
+ if (sessionID == null)
+ {
+ throw new IllegalArgumentException("'sessionID' cannot be null");
+ }
+ if (sessionID.length < 1 || sessionID.length > 32)
+ {
+ throw new IllegalArgumentException("'sessionID' must have length between 1 and 32 bytes, inclusive");
+ }
+
+ this.sessionID = Arrays.clone(sessionID);
+ this.sessionParameters = sessionParameters;
+ }
+
+ public synchronized SessionParameters exportSessionParameters()
+ {
+ return this.sessionParameters == null ? null : this.sessionParameters.copy();
+ }
+
+ public synchronized byte[] getSessionID()
+ {
+ return sessionID;
+ }
+
+ public synchronized void invalidate()
+ {
+ if (this.sessionParameters != null)
+ {
+ this.sessionParameters.clear();
+ this.sessionParameters = null;
+ }
+ }
+
+ public synchronized boolean isResumable()
+ {
+ return this.sessionParameters != null;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java
index 2b61507..68826d2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java
@@ -6,18 +6,29 @@ import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
public interface TlsSigner
{
-
void init(TlsContext context);
byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
throws CryptoException;
+ byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm,
+ AsymmetricKeyParameter privateKey, byte[] hash)
+ throws CryptoException;
+
boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
throws CryptoException;
+ boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+ AsymmetricKeyParameter publicKey, byte[] hash)
+ throws CryptoException;
+
Signer createSigner(AsymmetricKeyParameter privateKey);
+ Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey);
+
Signer createVerifyer(AsymmetricKeyParameter publicKey);
+ Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey);
+
boolean isValidPublicKey(AsymmetricKeyParameter publicKey);
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java
index 7067fa2..39ee99c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java
@@ -5,6 +5,8 @@ import java.io.IOException;
public interface TlsSignerCredentials
extends TlsCredentials
{
- byte[] generateCertificateSignature(byte[] md5andsha1)
+ byte[] generateCertificateSignature(byte[] hash)
throws IOException;
+
+ SignatureAndHashAlgorithm getSignatureAndHashAlgorithm();
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
index 1755c2d..178731d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
@@ -11,6 +11,8 @@ import org.bouncycastle.util.Arrays;
public class TlsStreamCipher
implements TlsCipher
{
+ private static boolean encryptThenMAC = false;
+
protected TlsContext context;
protected StreamCipher encryptCipher;
@@ -20,11 +22,9 @@ public class TlsStreamCipher
protected TlsMac readMac;
public TlsStreamCipher(TlsContext context, StreamCipher clientWriteCipher,
- StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest,
- int cipherKeySize)
- throws IOException
+ StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest,
+ int cipherKeySize) throws IOException
{
-
boolean isServer = context.isServer();
this.context = context;
@@ -89,38 +89,75 @@ public class TlsStreamCipher
public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
{
- byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+ /*
+ * TODO[draft-josefsson-salsa20-tls-02] Note that Salsa20 requires a 64-bit nonce. That
+ * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS
+ * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation
+ * of the 16-bit epoch with the 48-bit sequence number.
+ */
- byte[] outbuf = new byte[len + mac.length];
+ byte[] outBuf = new byte[len + writeMac.getSize()];
- encryptCipher.processBytes(plaintext, offset, len, outbuf, 0);
- encryptCipher.processBytes(mac, 0, mac.length, outbuf, len);
+ encryptCipher.processBytes(plaintext, offset, len, outBuf, 0);
+
+ if (encryptThenMAC)
+ {
+ byte[] mac = writeMac.calculateMac(seqNo, type, outBuf, 0, len);
+ System.arraycopy(mac, 0, outBuf, len, mac.length);
+ }
+ else
+ {
+ byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+ encryptCipher.processBytes(mac, 0, mac.length, outBuf, len);
+ }
- return outbuf;
+ return outBuf;
}
public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
throws IOException
{
+ /*
+ * TODO[draft-josefsson-salsa20-tls-02] Note that Salsa20 requires a 64-bit nonce. That
+ * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS
+ * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation
+ * of the 16-bit epoch with the 48-bit sequence number.
+ */
+
int macSize = readMac.getSize();
if (len < macSize)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
}
- byte[] deciphered = new byte[len];
- decryptCipher.processBytes(ciphertext, offset, len, deciphered, 0);
+ int plaintextLength = len - macSize;
- int macInputLen = len - macSize;
+ if (encryptThenMAC)
+ {
+ int ciphertextEnd = offset + len;
+ checkMAC(seqNo, type, ciphertext, ciphertextEnd - macSize, ciphertextEnd, ciphertext, offset, plaintextLength);
+ byte[] deciphered = new byte[plaintextLength];
+ decryptCipher.processBytes(ciphertext, offset, plaintextLength, deciphered, 0);
+ return deciphered;
+ }
+ else
+ {
+ byte[] deciphered = new byte[len];
+ decryptCipher.processBytes(ciphertext, offset, len, deciphered, 0);
+ checkMAC(seqNo, type, deciphered, plaintextLength, len, deciphered, 0, plaintextLength);
+ return Arrays.copyOfRange(deciphered, 0, plaintextLength);
+ }
+ }
- byte[] receivedMac = Arrays.copyOfRange(deciphered, macInputLen, len);
- byte[] computedMac = readMac.calculateMac(seqNo, type, deciphered, 0, macInputLen);
+ private void checkMAC(long seqNo, short type, byte[] recBuf, int recStart, int recEnd, byte[] calcBuf, int calcOff, int calcLen)
+ throws IOException
+ {
+ byte[] receivedMac = Arrays.copyOfRange(recBuf, recStart, recEnd);
+ byte[] computedMac = readMac.calculateMac(seqNo, type, calcBuf, calcOff, calcLen);
if (!Arrays.constantTimeAreEqual(receivedMac, computedMac))
{
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
-
- return Arrays.copyOfRange(deciphered, 0, macInputLen);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
index 8b16210..dae9ff5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
@@ -9,7 +9,10 @@ import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Vector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.Extensions;
@@ -44,11 +47,72 @@ public class TlsUtils
public static final Integer EXT_signature_algorithms = Integers.valueOf(ExtensionType.signature_algorithms);
+ public static void checkUint8(short i) throws IOException
+ {
+ if (!isValidUint8(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public static void checkUint8(int i) throws IOException
+ {
+ if (!isValidUint8(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public static void checkUint16(int i) throws IOException
+ {
+ if (!isValidUint16(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public static void checkUint24(int i) throws IOException
+ {
+ if (!isValidUint24(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public static void checkUint32(long i) throws IOException
+ {
+ if (!isValidUint32(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public static void checkUint48(long i) throws IOException
+ {
+ if (!isValidUint48(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public static void checkUint64(long i) throws IOException
+ {
+ if (!isValidUint64(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
public static boolean isValidUint8(short i)
{
return (i & 0xFF) == i;
}
+ public static boolean isValidUint8(int i)
+ {
+ return (i & 0xFF) == i;
+ }
+
public static boolean isValidUint16(int i)
{
return (i & 0xFFFF) == i;
@@ -74,17 +138,43 @@ public class TlsUtils
return true;
}
+ public static boolean isSSL(TlsContext context)
+ {
+ return context.getServerVersion().isSSL();
+ }
+
+ public static boolean isTLSv11(TlsContext context)
+ {
+ return ProtocolVersion.TLSv11.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion());
+ }
+
+ public static boolean isTLSv12(TlsContext context)
+ {
+ return ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion());
+ }
+
public static void writeUint8(short i, OutputStream output)
throws IOException
{
output.write(i);
}
+ public static void writeUint8(int i, OutputStream output)
+ throws IOException
+ {
+ output.write(i);
+ }
+
public static void writeUint8(short i, byte[] buf, int offset)
{
buf[offset] = (byte)i;
}
+ public static void writeUint8(int i, byte[] buf, int offset)
+ {
+ buf[offset] = (byte)i;
+ }
+
public static void writeUint16(int i, OutputStream output)
throws IOException
{
@@ -130,6 +220,17 @@ public class TlsUtils
buf[offset + 3] = (byte)(i);
}
+ public static void writeUint48(long i, OutputStream output)
+ throws IOException
+ {
+ output.write((byte)(i >> 40));
+ output.write((byte)(i >> 32));
+ output.write((byte)(i >> 24));
+ output.write((byte)(i >> 16));
+ output.write((byte)(i >> 8));
+ output.write((byte)(i));
+ }
+
public static void writeUint48(long i, byte[] buf, int offset)
{
buf[offset] = (byte)(i >> 40);
@@ -143,14 +244,14 @@ public class TlsUtils
public static void writeUint64(long i, OutputStream output)
throws IOException
{
- output.write((int)(i >> 56));
- output.write((int)(i >> 48));
- output.write((int)(i >> 40));
- output.write((int)(i >> 32));
- output.write((int)(i >> 24));
- output.write((int)(i >> 16));
- output.write((int)(i >> 8));
- output.write((int)(i));
+ output.write((byte)(i >> 56));
+ output.write((byte)(i >> 48));
+ output.write((byte)(i >> 40));
+ output.write((byte)(i >> 32));
+ output.write((byte)(i >> 24));
+ output.write((byte)(i >> 16));
+ output.write((byte)(i >> 8));
+ output.write((byte)(i));
}
public static void writeUint64(long i, byte[] buf, int offset)
@@ -168,13 +269,15 @@ public class TlsUtils
public static void writeOpaque8(byte[] buf, OutputStream output)
throws IOException
{
- writeUint8((short)buf.length, output);
+ checkUint8(buf.length);
+ writeUint8(buf.length, output);
output.write(buf);
}
public static void writeOpaque16(byte[] buf, OutputStream output)
throws IOException
{
+ checkUint16(buf.length);
writeUint16(buf.length, output);
output.write(buf);
}
@@ -182,6 +285,7 @@ public class TlsUtils
public static void writeOpaque24(byte[] buf, OutputStream output)
throws IOException
{
+ checkUint24(buf.length);
writeUint24(buf.length, output);
output.write(buf);
}
@@ -195,6 +299,32 @@ public class TlsUtils
}
}
+ public static void writeUint8Array(short[] uints, byte[] buf, int offset)
+ throws IOException
+ {
+ for (int i = 0; i < uints.length; ++i)
+ {
+ writeUint8(uints[i], buf, offset);
+ ++offset;
+ }
+ }
+
+ public static void writeUint8ArrayWithUint8Length(short[] uints, OutputStream output)
+ throws IOException
+ {
+ checkUint8(uints.length);
+ writeUint8(uints.length, output);
+ writeUint8Array(uints, output);
+ }
+
+ public static void writeUint8ArrayWithUint8Length(short[] uints, byte[] buf, int offset)
+ throws IOException
+ {
+ checkUint8(uints.length);
+ writeUint8(uints.length, buf, offset);
+ writeUint8Array(uints, buf, offset + 1);
+ }
+
public static void writeUint16Array(int[] uints, OutputStream output)
throws IOException
{
@@ -204,6 +334,56 @@ public class TlsUtils
}
}
+ public static void writeUint16Array(int[] uints, byte[] buf, int offset)
+ throws IOException
+ {
+ for (int i = 0; i < uints.length; ++i)
+ {
+ writeUint16(uints[i], buf, offset);
+ offset += 2;
+ }
+ }
+
+ public static void writeUint16ArrayWithUint16Length(int[] uints, OutputStream output)
+ throws IOException
+ {
+ int length = 2 * uints.length;
+ checkUint16(length);
+ writeUint16(length, output);
+ writeUint16Array(uints, output);
+ }
+
+ public static void writeUint16ArrayWithUint16Length(int[] uints, byte[] buf, int offset)
+ throws IOException
+ {
+ int length = 2 * uints.length;
+ checkUint16(length);
+ writeUint16(length, buf, offset);
+ writeUint16Array(uints, buf, offset + 2);
+ }
+
+ public static byte[] encodeOpaque8(byte[] buf)
+ throws IOException
+ {
+ checkUint8(buf.length);
+ return Arrays.prepend(buf, (byte)buf.length);
+ }
+
+ public static byte[] encodeUint8ArrayWithUint8Length(short[] uints) throws IOException
+ {
+ byte[] result = new byte[1 + uints.length];
+ writeUint8ArrayWithUint8Length(uints, result, 0);
+ return result;
+ }
+
+ public static byte[] encodeUint16ArrayWithUint16Length(int[] uints) throws IOException
+ {
+ int length = 2 * uints.length;
+ byte[] result = new byte[2 + length];
+ writeUint16ArrayWithUint16Length(uints, result, 0);
+ return result;
+ }
+
public static short readUint8(InputStream input)
throws IOException
{
@@ -297,6 +477,26 @@ public class TlsUtils
return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL);
}
+ public static byte[] readAllOrNothing(int length, InputStream input)
+ throws IOException
+ {
+ if (length < 1)
+ {
+ return EMPTY_BYTES;
+ }
+ byte[] buf = new byte[length];
+ int read = Streams.readFully(input, buf);
+ if (read == 0)
+ {
+ return null;
+ }
+ if (read != length)
+ {
+ throw new EOFException();
+ }
+ return buf;
+ }
+
public static byte[] readFully(int length, InputStream input)
throws IOException
{
@@ -383,6 +583,12 @@ public class TlsUtils
return ProtocolVersion.get(i1, i2);
}
+ public static int readVersionRaw(byte[] buf, int offset)
+ throws IOException
+ {
+ return (buf[offset] << 8) | buf[offset + 1];
+ }
+
public static int readVersionRaw(InputStream input)
throws IOException
{
@@ -395,6 +601,36 @@ public class TlsUtils
return (i1 << 8) | i2;
}
+ public static ASN1Primitive readASN1Object(byte[] encoding) throws IOException
+ {
+ ASN1InputStream asn1 = new ASN1InputStream(encoding);
+ ASN1Primitive result = asn1.readObject();
+ if (null == result)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ if (null != asn1.readObject())
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ return result;
+ }
+
+ public static ASN1Primitive readDERObject(byte[] encoding) throws IOException
+ {
+ /*
+ * NOTE: The current ASN.1 parsing code can't enforce DER-only parsing, but since DER is
+ * canonical, we can check it by re-encoding the result and comparing to the original.
+ */
+ ASN1Primitive result = readASN1Object(encoding);
+ byte[] check = result.getEncoded(ASN1Encoding.DER);
+ if (!Arrays.areEqual(check, encoding))
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+ return result;
+ }
+
public static void writeGMTUnixTime(byte[] buf, int offset)
{
int t = (int)(System.currentTimeMillis() / 1000L);
@@ -412,7 +648,6 @@ public class TlsUtils
}
public static void writeVersion(ProtocolVersion version, byte[] buf, int offset)
- throws IOException
{
buf[offset] = (byte)version.getMajorVersion();
buf[offset + 1] = (byte)version.getMinorVersion();
@@ -433,6 +668,31 @@ public class TlsUtils
return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa));
}
+ public static byte[] getExtensionData(Hashtable extensions, Integer extensionType)
+ {
+ return extensions == null ? null : (byte[])extensions.get(extensionType);
+ }
+
+ public static boolean hasExpectedEmptyExtensionData(Hashtable extensions, Integer extensionType,
+ short alertDescription) throws IOException
+ {
+ byte[] extension_data = getExtensionData(extensions, extensionType);
+ if (extension_data == null)
+ {
+ return false;
+ }
+ if (extension_data.length != 0)
+ {
+ throw new TlsFatalAlert(alertDescription);
+ }
+ return true;
+ }
+
+ public static TlsSession importSession(byte[] sessionID, SessionParameters sessionParameters)
+ {
+ return new TlsSessionImpl(sessionID, sessionParameters);
+ }
+
public static boolean isSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion)
{
return ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(clientVersion.getEquivalentTLSVersion());
@@ -461,17 +721,8 @@ public class TlsUtils
public static Vector getSignatureAlgorithmsExtension(Hashtable extensions)
throws IOException
{
-
- if (extensions == null)
- {
- return null;
- }
- byte[] extensionValue = (byte[])extensions.get(EXT_signature_algorithms);
- if (extensionValue == null)
- {
- return null;
- }
- return readSignatureAlgorithmsExtension(extensionValue);
+ byte[] extensionData = getExtensionData(extensions, EXT_signature_algorithms);
+ return extensionData == null ? null : readSignatureAlgorithmsExtension(extensionData);
}
/**
@@ -484,61 +735,94 @@ public class TlsUtils
public static byte[] createSignatureAlgorithmsExtension(Vector supportedSignatureAlgorithms)
throws IOException
{
-
- if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.size() < 1 || supportedSignatureAlgorithms.size() >= (1 << 15))
- {
- throw new IllegalArgumentException(
- "'supportedSignatureAlgorithms' must have length from 1 to (2^15 - 1)");
- }
-
ByteArrayOutputStream buf = new ByteArrayOutputStream();
// supported_signature_algorithms
- TlsUtils.writeUint16(2 * supportedSignatureAlgorithms.size(), buf);
- for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
- {
- SignatureAndHashAlgorithm entry = (SignatureAndHashAlgorithm)supportedSignatureAlgorithms.elementAt(i);
- entry.encode(buf);
- }
+ encodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf);
return buf.toByteArray();
}
/**
- * Read a 'signature_algorithms' extension value.
+ * Read 'signature_algorithms' extension data.
*
- * @param extensionValue The extension value.
+ * @param extensionData The extension data.
* @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
* @throws IOException
*/
- public static Vector readSignatureAlgorithmsExtension(byte[] extensionValue)
+ public static Vector readSignatureAlgorithmsExtension(byte[] extensionData)
throws IOException
{
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
- if (extensionValue == null)
+ // supported_signature_algorithms
+ Vector supported_signature_algorithms = parseSupportedSignatureAlgorithms(false, buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ return supported_signature_algorithms;
+ }
+
+ public static void encodeSupportedSignatureAlgorithms(Vector supportedSignatureAlgorithms, boolean allowAnonymous,
+ OutputStream output) throws IOException
+ {
+ if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.size() < 1
+ || supportedSignatureAlgorithms.size() >= (1 << 15))
{
- throw new IllegalArgumentException("'extensionValue' cannot be null");
+ throw new IllegalArgumentException(
+ "'supportedSignatureAlgorithms' must have length from 1 to (2^15 - 1)");
}
- ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+ // supported_signature_algorithms
+ int length = 2 * supportedSignatureAlgorithms.size();
+ TlsUtils.checkUint16(length);
+ TlsUtils.writeUint16(length, output);
+ for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
+ {
+ SignatureAndHashAlgorithm entry = (SignatureAndHashAlgorithm)supportedSignatureAlgorithms.elementAt(i);
+ if (!allowAnonymous && entry.getSignature() == SignatureAlgorithm.anonymous)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used
+ * in Section 7.4.3. It MUST NOT appear in this extension.
+ */
+ throw new IllegalArgumentException(
+ "SignatureAlgorithm.anonymous MUST NOT appear in the signature_algorithms extension");
+ }
+ entry.encode(output);
+ }
+ }
+ public static Vector parseSupportedSignatureAlgorithms(boolean allowAnonymous, InputStream input)
+ throws IOException
+ {
// supported_signature_algorithms
- int length = TlsUtils.readUint16(buf);
+ int length = TlsUtils.readUint16(input);
if (length < 2 || (length & 1) != 0)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
}
int count = length / 2;
- Vector result = new Vector(count);
+ Vector supportedSignatureAlgorithms = new Vector(count);
for (int i = 0; i < count; ++i)
{
- SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.parse(buf);
- result.addElement(entry);
+ SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.parse(input);
+ if (!allowAnonymous && entry.getSignature() == SignatureAlgorithm.anonymous)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used
+ * in Section 7.4.3. It MUST NOT appear in this extension.
+ */
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ supportedSignatureAlgorithms.addElement(entry);
}
-
- TlsProtocol.assertEmpty(buf);
-
- return result;
+ return supportedSignatureAlgorithms;
}
public static byte[] PRF(TlsContext context, byte[] secret, String asciiLabel, byte[] seed, int size)
@@ -557,12 +841,7 @@ public class TlsUtils
if (prfAlgorithm == PRFAlgorithm.tls_prf_legacy)
{
- if (!ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(version.getEquivalentTLSVersion()))
- {
- return PRF_legacy(secret, label, labelSeed, size);
- }
-
- prfAlgorithm = PRFAlgorithm.tls_prf_sha256;
+ return PRF_legacy(secret, label, labelSeed, size);
}
Digest prfDigest = createPRFHash(prfAlgorithm);
@@ -646,7 +925,7 @@ public class TlsUtils
byte[] seed = concat(securityParameters.getServerRandom(),
securityParameters.getClientRandom());
- if (context.getServerVersion().isSSL())
+ if (isSSL(context))
{
return calculateKeyBlock_SSL(master_secret, seed, size);
}
@@ -690,7 +969,7 @@ public class TlsUtils
SecurityParameters securityParameters = context.getSecurityParameters();
byte[] seed = concat(securityParameters.getClientRandom(), securityParameters.getServerRandom());
- if (context.getServerVersion().isSSL())
+ if (isSSL(context))
{
return calculateMasterSecret_SSL(pre_master_secret, seed);
}
@@ -729,7 +1008,7 @@ public class TlsUtils
static byte[] calculateVerifyData(TlsContext context, String asciiLabel, byte[] handshakeHash)
{
- if (context.getServerVersion().isSSL())
+ if (isSSL(context))
{
return handshakeHash;
}
@@ -741,7 +1020,7 @@ public class TlsUtils
return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length);
}
- public static final Digest createHash(int hashAlgorithm)
+ public static final Digest createHash(short hashAlgorithm)
{
switch (hashAlgorithm)
{
@@ -762,7 +1041,7 @@ public class TlsUtils
}
}
- public static final Digest cloneHash(int hashAlgorithm, Digest hash)
+ public static final Digest cloneHash(short hashAlgorithm, Digest hash)
{
switch (hashAlgorithm)
{
@@ -820,7 +1099,7 @@ public class TlsUtils
}
}
- public static ASN1ObjectIdentifier getOIDForHashAlgorithm(int hashAlgorithm)
+ public static ASN1ObjectIdentifier getOIDForHashAlgorithm(short hashAlgorithm)
{
switch (hashAlgorithm)
{
@@ -912,6 +1191,20 @@ public class TlsUtils
throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
}
+ static void trackHashAlgorithms(TlsHandshakeHash handshakeHash, Vector supportedSignatureAlgorithms)
+ {
+ if (supportedSignatureAlgorithms != null)
+ {
+ for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = (SignatureAndHashAlgorithm)
+ supportedSignatureAlgorithms.elementAt(i);
+ short hashAlgorithm = signatureAndHashAlgorithm.getHash();
+ handshakeHash.trackHashAlgorithm(hashAlgorithm);
+ }
+ }
+ }
+
public static boolean hasSigningCapability(short clientCertificateType)
{
switch (clientCertificateType)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java
index f3dd59e..d5f0769 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java
@@ -7,18 +7,16 @@ import java.net.DatagramSocket;
public class UDPTransport
implements DatagramTransport
{
+ protected final static int MIN_IP_OVERHEAD = 20;
+ protected final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64;
+ protected final static int UDP_OVERHEAD = 8;
- private final static int MIN_IP_OVERHEAD = 20;
- private final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64;
- private final static int UDP_OVERHEAD = 8;
-
- private final DatagramSocket socket;
- private final int receiveLimit, sendLimit;
+ protected final DatagramSocket socket;
+ protected final int receiveLimit, sendLimit;
public UDPTransport(DatagramSocket socket, int mtu)
throws IOException
{
-
if (!socket.isBound() || !socket.isConnected())
{
throw new IllegalArgumentException("'socket' must be bound and connected");
@@ -62,7 +60,7 @@ public class UDPTransport
* the DTLS implementation SHOULD generate an error, thus avoiding sending a packet
* which will be fragmented."
*/
- // TODO Exception
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
DatagramPacket packet = new DatagramPacket(buf, off, len);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java
new file mode 100644
index 0000000..c32a904
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Strings;
+
+/**
+ * RFC 6066 5.
+ */
+public class URLAndHash
+{
+ protected String url;
+ protected byte[] sha1Hash;
+
+ public URLAndHash(String url, byte[] sha1Hash)
+ {
+ if (url == null || url.length() < 1 || url.length() >= (1 << 16))
+ {
+ throw new IllegalArgumentException("'url' must have length from 1 to (2^16 - 1)");
+ }
+ if (sha1Hash != null && sha1Hash.length != 20)
+ {
+ throw new IllegalArgumentException("'sha1Hash' must have length == 20, if present");
+ }
+
+ this.url = url;
+ this.sha1Hash = sha1Hash;
+ }
+
+ public String getURL()
+ {
+ return url;
+ }
+
+ public byte[] getSHA1Hash()
+ {
+ return sha1Hash;
+ }
+
+ /**
+ * Encode this {@link URLAndHash} to an {@link OutputStream}.
+ *
+ * @param output the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output)
+ throws IOException
+ {
+ byte[] urlEncoding = Strings.toByteArray(this.url);
+ TlsUtils.writeOpaque16(urlEncoding, output);
+
+ if (this.sha1Hash == null)
+ {
+ TlsUtils.writeUint8(0, output);
+ }
+ else
+ {
+ TlsUtils.writeUint8(1, output);
+ output.write(this.sha1Hash);
+ }
+ }
+
+ /**
+ * Parse a {@link URLAndHash} from an {@link InputStream}.
+ *
+ * @param context
+ * the {@link TlsContext} of the current connection.
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link URLAndHash} object.
+ * @throws IOException
+ */
+ public static URLAndHash parse(TlsContext context, InputStream input)
+ throws IOException
+ {
+ byte[] urlEncoding = TlsUtils.readOpaque16(input);
+ if (urlEncoding.length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ String url = Strings.fromByteArray(urlEncoding);
+
+ byte[] sha1Hash = null;
+ short padding = TlsUtils.readUint8(input);
+ switch (padding)
+ {
+ case 0:
+ if (TlsUtils.isTLSv12(context))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ break;
+ case 1:
+ sha1Hash = TlsUtils.readFully(20, input);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ return new URLAndHash(url, sha1Hash);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
index bfa304b..6bf3399 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -10,7 +10,6 @@ import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.oiw.ElGamalParameter;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.DHParameter;
@@ -18,11 +17,9 @@ import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
import org.bouncycastle.asn1.sec.ECPrivateKey;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
-import org.bouncycastle.asn1.x9.X962NamedCurves;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -130,22 +127,7 @@ public class PrivateKeyFactory
if (params.isNamedCurve())
{
ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
- x9 = X962NamedCurves.getByOID(oid);
-
- if (x9 == null)
- {
- x9 = SECNamedCurves.getByOID(oid);
-
- if (x9 == null)
- {
- x9 = NISTNamedCurves.getByOID(oid);
-
- if (x9 == null)
- {
- x9 = TeleTrusTNamedCurves.getByOID(oid);
- }
- }
- }
+ x9 = ECNamedCurveTable.getByOID(oid);
}
else
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
index ab52802..7b06c3f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
@@ -2,17 +2,23 @@ package org.bouncycastle.crypto.util;
import java.io.IOException;
+import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.sec.ECPrivateKey;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
@@ -43,6 +49,31 @@ public class PrivateKeyInfoFactory
return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(priv.getX()));
}
+ else if (privateKey instanceof ECPrivateKeyParameters)
+ {
+ ECPrivateKeyParameters priv = (ECPrivateKeyParameters)privateKey;
+ ECDomainParameters domainParams = priv.getParameters();
+ ASN1Encodable params;
+
+ // TODO: need to handle named curves
+ if (domainParams == null)
+ {
+ params = new X962Parameters(DERNull.INSTANCE); // Implicitly CA
+ }
+ else
+ {
+ X9ECParameters ecP = new X9ECParameters(
+ domainParams.getCurve(),
+ domainParams.getG(),
+ domainParams.getN(),
+ domainParams.getH(),
+ domainParams.getSeed());
+
+ params = new X962Parameters(ecP);
+ }
+
+ return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), new ECPrivateKey(priv.getD(), params));
+ }
else
{
throw new IOException("key parameters not recognised.");
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
index 343bbd3..2d2927b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -12,14 +12,11 @@ import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.oiw.ElGamalParameter;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.DHParameter;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.RSAPublicKey;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -27,7 +24,7 @@ import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.asn1.x9.DHDomainParameters;
import org.bouncycastle.asn1.x9.DHPublicKey;
import org.bouncycastle.asn1.x9.DHValidationParms;
-import org.bouncycastle.asn1.x9.X962NamedCurves;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
@@ -160,29 +157,13 @@ public class PublicKeyFactory
}
else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey))
{
- X962Parameters params = new X962Parameters(
- (ASN1Primitive)algId.getParameters());
+ X962Parameters params = X962Parameters.getInstance(algId.getParameters());
X9ECParameters x9;
if (params.isNamedCurve())
{
ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
- x9 = X962NamedCurves.getByOID(oid);
-
- if (x9 == null)
- {
- x9 = SECNamedCurves.getByOID(oid);
-
- if (x9 == null)
- {
- x9 = NISTNamedCurves.getByOID(oid);
-
- if (x9 == null)
- {
- x9 = TeleTrusTNamedCurves.getByOID(oid);
- }
- }
- }
+ x9 = ECNamedCurveTable.getByOID(oid);
}
else
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html
deleted file mode 100644
index 787b892..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Some general utility/conversion classes.
-</body>
-</html>