summaryrefslogtreecommitdiff
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.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/SkippingCipher.java31
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/SkippingStreamCipher.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/StreamCipher.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java16
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java90
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java21
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java27
-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/DHKDFParameters.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java65
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java66
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6StandardGroups.java157
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java63
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/AllTests.java25
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEParticipantTest.java561
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEPrimeOrderGroupTest.java85
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEUtilTest.java267
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java17
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java23
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java68
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java41
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java47
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java48
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java24
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java320
-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.java25
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java27
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java28
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java27
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/test/AllTests.java39
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/test/ECElGamalTest.java88
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/ec/test/ECTransformationTest.java149
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java164
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java91
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java299
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java89
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapPadEngine.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java147
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCiphertext.java147
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCoreEngine.java309
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java28
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java98
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java22
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC5649WrapEngine.java300
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java326
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java22
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java12
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/examples/DESExample.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java660
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupKeyPairGenerator.java63
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupParametersGenerator.java124
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java28
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java24
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java26
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java36
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java25
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java34
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenBSDBCrypt.java315
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java204
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java57
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/io/CipherIOException.java26
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java234
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java50
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java13
-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.java83
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java45
-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.java39
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/CMacWithIV.java27
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/GMac.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java77
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/SipHash.java110
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java22
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java39
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java124
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java49
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java25
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java120
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java93
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java337
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java67
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java264
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java215
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/package.html5
-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/PKCS7Padding.java17
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupKeyGenerationParameters.java29
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupKeyParameters.java40
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupParameters.java53
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupPrivateKeyParameters.java61
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupPublicKeyParameters.java42
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/ECNamedDomainParameters.java35
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java86
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/SRP6GroupParameters.java24
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java48
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/params/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/parsers/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/X931RNG.java146
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandom.java63
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandomBuilder.java97
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java25
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java21
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java25
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java7
-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/prng/test/AllTests.java39
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java528
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/DRBGTestVector.java131
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/DualECDRBGTest.java415
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/FixedSecureRandomTest.java68
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/HMacDRBGTest.java508
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/HashDRBGTest.java481
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/RegressionTest.java34
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/SP800RandomTest.java319
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/TestEntropySourceProvider.java46
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/X931Test.java124
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/prng/test/X931TestVector.java56
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java10
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java56
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java57
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java59
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java38
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java16
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java37
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/X931Signer.java269
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/AEADTestUtil.java474
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/AESFastTest.java150
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/AESLightTest.java150
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/AESTest.java442
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/AESVectorFileTest.java258
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/AESWrapPadTest.java146
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/AESWrapTest.java260
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/AllTests.java41
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/BCryptTest.java151
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/BigSkippingCipherTest.java223
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherMonteCarloTest.java82
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherResetTest.java206
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherVectorTest.java71
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/BlowfishTest.java57
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CAST5Test.java44
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CAST6Test.java44
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CCMTest.java305
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CMacTest.java321
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CTSTest.java212
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CamelliaLightTest.java66
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CamelliaTest.java70
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ChaChaTest.java403
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CipherStreamTest.java706
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CipherTest.java117
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/CramerShoupTest.java147
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DESTest.java206
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DESedeTest.java177
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DHKEKGeneratorTest.java70
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DHTest.java414
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DSATest.java602
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU4145Test.java278
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DeterministicDSATest.java513
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DigestRandomNumberTest.java152
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/DigestTest.java226
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/EAXTest.java355
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ECDHKEKGeneratorTest.java71
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ECGOST3410Test.java327
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java138
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESTest.java379
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ECNRTest.java99
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ECTest.java926
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ElGamalTest.java285
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/EqualsHashCodeTest.java261
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/GCMReorderTest.java347
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/GCMTest.java687
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/GMacTest.java174
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/GOST28147MacTest.java89
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/GOST28147Test.java328
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3410Test.java1570
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3411DigestTest.java74
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Grain128Test.java117
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Grainv1Test.java140
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/HCFamilyTest.java192
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/HCFamilyVecTest.java199
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/HKDFGeneratorTest.java304
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/HashCommitmentTest.java152
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/IDEATest.java38
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ISAACTest.java180
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ISO9796Test.java972
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ISO9797Alg3MacTest.java126
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/KDF1GeneratorTest.java93
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/KDF2GeneratorTest.java105
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/KDFCounterGeneratorTest.java51
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/KDFDoublePipelineIteratorGeneratorTest.java72
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/KDFFeedbackGeneratorTest.java71
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/MD2DigestTest.java52
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/MD4DigestTest.java43
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/MD5DigestTest.java43
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/MD5HMacTest.java98
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/MGF1GeneratorTest.java88
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/MacTest.java181
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ModeTest.java115
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/NISTCTSTest.java160
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/NaccacheSternTest.java354
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/NoekeonTest.java45
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/NonMemoableDigestTest.java112
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/NullTest.java77
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/OAEPTest.java830
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/OCBTest.java520
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/OpenBSDBCryptTest.java141
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/PKCS12Test.java206
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/PKCS5Test.java265
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/PSSBlindTest.java398
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/PSSTest.java332
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/PaddingTest.java200
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Poly1305Test.java388
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RC2Test.java66
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RC2WrapTest.java111
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RC4Test.java45
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RC5Test.java188
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RC6Test.java64
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RFC3211WrapTest.java220
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD128DigestTest.java58
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD128HMacTest.java86
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD160DigestTest.java58
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD160HMacTest.java86
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD256DigestTest.java58
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD320DigestTest.java58
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RSABlindedTest.java437
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java55
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java61
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RSATest.java498
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RegressionTest.java161
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ResetTest.java99
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/RijndaelTest.java116
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SCryptTest.java144
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SEEDTest.java53
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA1DigestTest.java48
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA1HMacTest.java111
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA224DigestTest.java59
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA224HMacTest.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA256DigestTest.java60
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA256HMacTest.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA384DigestTest.java59
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA384HMacTest.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA3DigestTest.java363
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512DigestTest.java60
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512HMacTest.java108
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512t224DigestTest.java60
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512t256DigestTest.java60
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SM3DigestTest.java57
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SRP6Test.java267
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Salsa20Test.java400
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SerpentTest.java103
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Shacal2Test.java200
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/ShortenedDigestTest.java89
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SipHashTest.java143
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SkeinDigestTest.java294
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SkeinMacTest.java162
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/SkipjackTest.java35
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/StreamCipherResetTest.java133
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/StreamCipherVectorTest.java62
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/TEATest.java48
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish1024Test.java60
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish256Test.java45
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish512Test.java50
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/TigerDigestTest.java59
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/TwofishTest.java45
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCKSA3Test.java97
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCMacTest.java51
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCTest.java97
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/WhirlpoolDigestTest.java105
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/X931SignerTest.java138
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/XSalsa20Test.java166
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/XTEATest.java48
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/test/package.html5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java78
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java27
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java48
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java87
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java20
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/AlwaysValidVerifyer.java24
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/BasicTlsPSKIdentity.java42
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java26
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java13
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateVerifyer.java16
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/Chacha20Poly1305.java156
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java147
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherType.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java143
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java20
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java15
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java58
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java92
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java137
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java21
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSRPGroupVerifier.java67
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java237
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestAlgorithm.java23
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java22
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java29
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/FiniteFieldDHEGroup.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsAuthentication.java28
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsClient.java33
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java82
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsServer.java346
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java41
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java32
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java52
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsServer.java166
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java40
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerSRPParams.java75
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java58
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/SimulatedTlsSRPIdentityManager.java69
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java30
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java1
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java172
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java27
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java52
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java410
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java19
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java283
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java89
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java9
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java5
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java91
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java3
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java11
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKIdentityManager.java8
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java134
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java15
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java170
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocolHandler.java23
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java61
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRuntimeException.java26
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPGroupVerifier.java14
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPIdentityManager.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java185
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPLoginParameters.java34
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java35
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java4
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java96
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java67
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java754
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/AllTests.java23
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/BasicTlsTest.java226
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSClientTest.java81
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSProtocolTest.java102
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSServerTest.java75
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestCase.java161
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestSuite.java126
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/HTTPSServerThread.java113
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/KeyStores.java113
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/LoggingDatagramTransport.java91
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSClient.java180
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSServer.java138
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDatagramAssociation.java110
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockPSKTlsClient.java134
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockPSKTlsServer.java110
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockSRPTlsClient.java120
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockSRPTlsServer.java124
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsClient.java170
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsServer.java143
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/NetworkInputStream.java60
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/NetworkOutputStream.java54
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/PSKTlsClientTest.java82
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsClientTest.java70
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsPSKProtocolTest.java81
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolTest.java82
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsSRPProtocolTest.java81
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsServerTest.java82
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestCase.java171
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientImpl.java251
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestConfig.java101
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerImpl.java198
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestSuite.java111
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestUtils.java192
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/tls/test/UnreliableDatagramTransport.java93
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/Pack.java12
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java25
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java12
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java23
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java6
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/util/package.html5
487 files changed, 48036 insertions, 3008 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
index dd056aca..39f59da8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
@@ -48,13 +48,13 @@ public class BufferedBlockCipher
pgpCFB = (idx > 0 && name.startsWith("PGP", idx));
- if (pgpCFB)
+ if (pgpCFB || cipher instanceof StreamCipher)
{
partialBlockOkay = true;
}
else
{
- 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)));
+ partialBlockOkay = (idx > 0 && (name.startsWith("OpenPGP", idx)));
}
}
@@ -141,7 +141,7 @@ public class BufferedBlockCipher
}
/**
- * 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.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/SkippingCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/SkippingCipher.java
new file mode 100644
index 00000000..f8cc648e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/SkippingCipher.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.crypto;
+
+/**
+ * Ciphers producing a key stream which can be reset to particular points in the stream implement this.
+ */
+public interface SkippingCipher
+{
+ /**
+ * Skip numberOfBytes forwards, or backwards.
+ *
+ * @param numberOfBytes the number of bytes to skip (positive forward, negative backwards).
+ * @return the number of bytes actually skipped.
+ * @throws java.lang.IllegalArgumentException if numberOfBytes is an invalid value.
+ */
+ long skip(long numberOfBytes);
+
+ /**
+ * Reset the cipher and then skip forward to a given position.
+ *
+ * @param position the number of bytes in to set the cipher state to.
+ * @return the byte position moved to.
+ */
+ long seekTo(long position);
+
+ /**
+ * Return the current "position" of the cipher
+ *
+ * @return the current byte position.
+ */
+ long getPosition();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/SkippingStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/SkippingStreamCipher.java
new file mode 100644
index 00000000..a707a810
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/SkippingStreamCipher.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.crypto;
+
+/**
+ * General interface for a stream cipher that supports skipping.
+ */
+public interface SkippingStreamCipher
+ extends StreamCipher, SkippingCipher
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
index 8fdd2324..09aadfbf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/StreamBlockCipher.java
@@ -1,108 +1,58 @@
package org.bouncycastle.crypto;
/**
- * a wrapper for block ciphers with a single byte block size, so that they
- * can be treated like stream ciphers.
+ * A parent class for block cipher modes that do not require block aligned data to be processed, but can function in
+ * a streaming mode.
*/
-public class StreamBlockCipher
- implements StreamCipher
+public abstract class StreamBlockCipher
+ implements BlockCipher, StreamCipher
{
- private BlockCipher cipher;
+ private final BlockCipher cipher;
- private byte[] oneByte = new byte[1];
-
- /**
- * basic constructor.
- *
- * @param cipher the block cipher to be wrapped.
- * @exception IllegalArgumentException if the cipher has a block size other than
- * one.
- */
- public StreamBlockCipher(
- BlockCipher cipher)
+ protected StreamBlockCipher(BlockCipher cipher)
{
- if (cipher.getBlockSize() != 1)
- {
- throw new IllegalArgumentException("block cipher block size != 1.");
- }
-
this.cipher = cipher;
}
/**
- * initialise the underlying cipher.
+ * return the underlying block cipher that we are wrapping.
*
- * @param forEncryption true if we are setting up for encryption, false otherwise.
- * @param params the necessary parameters for the underlying cipher to be initialised.
+ * @return the underlying block cipher that we are wrapping.
*/
- public void init(
- boolean forEncryption,
- CipherParameters params)
+ public BlockCipher getUnderlyingCipher()
{
- cipher.init(forEncryption, params);
+ return cipher;
}
- /**
- * return the name of the algorithm we are wrapping.
- *
- * @return the name of the algorithm we are wrapping.
- */
- public String getAlgorithmName()
+ public final byte returnByte(byte in)
{
- return cipher.getAlgorithmName();
+ return calculateByte(in);
}
- /**
- * encrypt/decrypt a single byte returning the result.
- *
- * @param in the byte to be processed.
- * @return the result of processing the input byte.
- */
- public byte returnByte(
- byte in)
- {
- oneByte[0] = in;
-
- cipher.processBlock(oneByte, 0, oneByte, 0);
-
- return oneByte[0];
- }
-
- /**
- * process a block of bytes from in putting the result into out.
- *
- * @param in the input byte array.
- * @param inOff the offset into the in array where the data to be processed starts.
- * @param len the number of bytes to be processed.
- * @param out the output buffer the processed bytes go into.
- * @param outOff the offset into the output byte array the processed data stars at.
- * @exception DataLengthException if the output buffer is too small.
- */
- public void processBytes(
- byte[] in,
- int inOff,
- int len,
- byte[] out,
- int outOff)
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
throws DataLengthException
{
if (outOff + len > out.length)
{
- throw new DataLengthException("output buffer too small in processBytes()");
+ throw new DataLengthException("output buffer too short");
}
- for (int i = 0; i != len; i++)
+ if (inOff + len > in.length)
{
- cipher.processBlock(in, inOff + i, out, outOff + i);
+ throw new DataLengthException("input buffer too small");
}
- }
- /**
- * reset the underlying cipher. This leaves it in the same state
- * it was at after the last init (if there was one).
- */
- public void reset()
- {
- cipher.reset();
+ int inStart = inOff;
+ int inEnd = inOff + len;
+ int outStart = outOff;
+
+ while (inStart < inEnd)
+ {
+ out[outStart++] = calculateByte(in[inStart++]);
+ }
+
+ return len;
}
-}
+
+ protected abstract byte calculateByte(byte b);
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/StreamCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/StreamCipher.java
index 2a55d4f6..c1255e94 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/StreamCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/StreamCipher.java
@@ -40,9 +40,10 @@ public interface StreamCipher
* @param len the number of bytes to be processed.
* @param out the output buffer the processed bytes go into.
* @param outOff the offset into the output byte array the processed data starts at.
+ * @return the number of bytes produced - should always be len.
* @exception DataLengthException if the output buffer is too small.
*/
- public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
throws DataLengthException;
/**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java
index 638bcb13..da4951d1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/DHStandardGroups.java
@@ -10,20 +10,19 @@ import org.bouncycastle.util.encoders.Hex;
*/
public class DHStandardGroups
{
+ private static BigInteger fromHex(String hex)
+ {
+ return new BigInteger(1, Hex.decode(hex));
+ }
private static DHParameters fromPG(String hexP, String hexG)
{
- BigInteger p = new BigInteger(1, Hex.decode(hexP));
- BigInteger g = new BigInteger(1, Hex.decode(hexG));
- return new DHParameters(p, g);
+ return new DHParameters(fromHex(hexP), fromHex(hexG));
}
private static DHParameters fromPGQ(String hexP, String hexG, String hexQ)
{
- BigInteger p = new BigInteger(1, Hex.decode(hexP));
- BigInteger g = new BigInteger(1, Hex.decode(hexG));
- BigInteger q = new BigInteger(1, Hex.decode(hexQ));
- return new DHParameters(p, g, q);
+ return new DHParameters(fromHex(hexP), fromHex(hexG), fromHex(hexQ));
}
/*
@@ -115,7 +114,8 @@ public class DHStandardGroups
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
- + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF";
+ + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ + "6DCC4024FFFFFFFFFFFFFFFF";
private static final String rfc3526_6144_g = "02";
public static final DHParameters rfc3526_6144 = fromPG(rfc3526_6144_p, rfc3526_6144_g);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java
index 94efd92d..3fcb2df7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEParticipant.java
@@ -10,19 +10,16 @@ import org.bouncycastle.util.Arrays;
/**
* A participant in a Password Authenticated Key Exchange by Juggling (J-PAKE) exchange.
- * <p/>
- * <p/>
+ * <p>
* The J-PAKE exchange is defined by Feng Hao and Peter Ryan in the paper
* <a href="http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf">
* "Password Authenticated Key Exchange by Juggling, 2008."</a>
- * <p/>
- * <p/>
+ * <p>
* The J-PAKE protocol is symmetric.
* There is no notion of a <i>client</i> or <i>server</i>, but rather just two <i>participants</i>.
* An instance of {@link JPAKEParticipant} represents one participant, and
* is the primary interface for executing the exchange.
- * <p/>
- * <p/>
+ * <p>
* To execute an exchange, construct a {@link JPAKEParticipant} on each end,
* and call the following 7 methods
* (once and only once, in the given order, for each participant, sending messages between them as described):
@@ -35,36 +32,29 @@ import org.bouncycastle.util.Arrays;
* <li>{@link #createRound3PayloadToSend(BigInteger)} - and send the payload to the other participant</li>
* <li>{@link #validateRound3PayloadReceived(JPAKERound3Payload, BigInteger)} - use the payload received from the other participant</li>
* </ol>
- * <p/>
- * <p/>
+ * <p>
* Each side should derive a session key from the keying material returned by {@link #calculateKeyingMaterial()}.
* The caller is responsible for deriving the session key using a secure key derivation function (KDF).
- * <p/>
- * <p/>
+ * <p>
* Round 3 is an optional key confirmation process.
* If you do not execute round 3, then there is no assurance that both participants are using the same key.
* (i.e. if the participants used different passwords, then their session keys will differ.)
- * <p/>
- * <p/>
+ * <p>
* If the round 3 validation succeeds, then the keys are guaranteed to be the same on both sides.
- * <p/>
- * <p/>
+ * <p>
* The symmetric design can easily support the asymmetric cases when one party initiates the communication.
* e.g. Sometimes the round1 payload and round2 payload may be sent in one pass.
* Also, in some cases, the key confirmation payload can be sent together with the round2 payload.
* These are the trivial techniques to optimize the communication.
- * <p/>
- * <p/>
+ * <p>
* The key confirmation process is implemented as specified in
* <a href="http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf">NIST SP 800-56A Revision 1</a>,
* Section 8.2 Unilateral Key Confirmation for Key Agreement Schemes.
- * <p/>
- * <p/>
+ * <p>
* This class is stateful and NOT threadsafe.
* Each instance should only be used for ONE complete J-PAKE exchange
* (i.e. a new {@link JPAKEParticipant} should be constructed for each new J-PAKE exchange).
- * <p/>
- * <p/>
+ * <p>
* See {@link JPAKEExample} for example usage.
*/
public class JPAKEParticipant
@@ -91,9 +81,10 @@ public class JPAKEParticipant
/**
* Shared secret. This only contains the secret between construction
* and the call to {@link #calculateKeyingMaterial()}.
- * <p/>
+ * <p>
* i.e. When {@link #calculateKeyingMaterial()} is called, this buffer overwritten with 0's,
* and the field is set to null.
+ * </p>
*/
private char[] password;
@@ -155,7 +146,7 @@ public class JPAKEParticipant
* Convenience constructor for a new {@link JPAKEParticipant} that uses
* the {@link JPAKEPrimeOrderGroups#NIST_3072} prime order group,
* a SHA-256 digest, and a default {@link SecureRandom} implementation.
- * <p/>
+ * <p>
* After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
*
* @param participantId unique identifier of this participant.
@@ -180,7 +171,7 @@ public class JPAKEParticipant
/**
* Convenience constructor for a new {@link JPAKEParticipant} that uses
* a SHA-256 digest and a default {@link SecureRandom} implementation.
- * <p/>
+ * <p>
* After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
*
* @param participantId unique identifier of this participant.
@@ -209,7 +200,7 @@ public class JPAKEParticipant
/**
* Construct a new {@link JPAKEParticipant}.
- * <p/>
+ * <p>
* After construction, the {@link #getState() state} will be {@link #STATE_INITIALIZED}.
*
* @param participantId unique identifier of this participant.
@@ -278,8 +269,7 @@ public class JPAKEParticipant
/**
* Creates and returns the payload to send to the other participant during round 1.
- * <p/>
- * <p/>
+ * <p>
* After execution, the {@link #getState() state} will be {@link #STATE_ROUND_1_CREATED}.
*/
public JPAKERound1Payload createRound1PayloadToSend()
@@ -304,11 +294,9 @@ public class JPAKEParticipant
/**
* Validates the payload received from the other participant during round 1.
- * <p/>
- * <p/>
+ * <p>
* Must be called prior to {@link #createRound2PayloadToSend()}.
- * <p/>
- * <p/>
+ * <p>
* After execution, the {@link #getState() state} will be {@link #STATE_ROUND_1_VALIDATED}.
*
* @throws CryptoException if validation fails.
@@ -338,11 +326,9 @@ public class JPAKEParticipant
/**
* Creates and returns the payload to send to the other participant during round 2.
- * <p/>
- * <p/>
+ * <p>
* {@link #validateRound1PayloadReceived(JPAKERound1Payload)} must be called prior to this method.
- * <p/>
- * <p/>
+ * <p>
* After execution, the {@link #getState() state} will be {@link #STATE_ROUND_2_CREATED}.
*
* @throws IllegalStateException if called prior to {@link #validateRound1PayloadReceived(JPAKERound1Payload)}, or multiple times
@@ -370,16 +356,13 @@ public class JPAKEParticipant
/**
* Validates the payload received from the other participant during round 2.
- * <p/>
- * <p/>
+ * <p>
* Note that this DOES NOT detect a non-common password.
* The only indication of a non-common password is through derivation
* of different keys (which can be detected explicitly by executing round 3 and round 4)
- * <p/>
- * <p/>
+ * <p>
* Must be called prior to {@link #calculateKeyingMaterial()}.
- * <p/>
- * <p/>
+ * <p>
* After execution, the {@link #getState() state} will be {@link #STATE_ROUND_2_VALIDATED}.
*
* @throws CryptoException if validation fails.
@@ -412,8 +395,7 @@ public class JPAKEParticipant
* Calculates and returns the key material.
* A session key must be derived from this key material using a secure key derivation function (KDF).
* The KDF used to derive the key is handled externally (i.e. not by {@link JPAKEParticipant}).
- * <p/>
- * <p/>
+ * <p>
* The keying material will be identical for each participant if and only if
* each participant's password is the same. i.e. If the participants do not
* share the same password, then each participant will derive a different key.
@@ -422,17 +404,13 @@ public class JPAKEParticipant
* If you want to handle this detection explicitly, you can optionally perform
* rounds 3 and 4. See {@link JPAKEParticipant} for details on how to execute
* rounds 3 and 4.
- * <p/>
- * <p/>
+ * <p>
* The keying material will be in the range <tt>[0, p-1]</tt>.
- * <p/>
- * <p/>
+ * <p>
* {@link #validateRound2PayloadReceived(JPAKERound2Payload)} must be called prior to this method.
- * <p/>
- * <p/>
+ * <p>
* As a side effect, the internal {@link #password} array is cleared, since it is no longer needed.
- * <p/>
- * <p/>
+ * <p>
* After execution, the {@link #getState() state} will be {@link #STATE_KEY_CALCULATED}.
*
* @throws IllegalStateException if called prior to {@link #validateRound2PayloadReceived(JPAKERound2Payload)},
@@ -484,11 +462,9 @@ public class JPAKEParticipant
/**
* Creates and returns the payload to send to the other participant during round 3.
- * <p/>
- * <p/>
+ * <p>
* See {@link JPAKEParticipant} for more details on round 3.
- * <p/>
- * <p/>
+ * <p>
* After execution, the {@link #getState() state} will be {@link #STATE_ROUND_3_CREATED}.
*
* @param keyingMaterial The keying material as returned from {@link #calculateKeyingMaterial()}.
@@ -522,11 +498,9 @@ public class JPAKEParticipant
/**
* Validates the payload received from the other participant during round 3.
- * <p/>
- * <p/>
+ * <p>
* See {@link JPAKEParticipant} for more details on round 3.
- * <p/>
- * <p/>
+ * <p>
* After execution, the {@link #getState() state} will be {@link #STATE_ROUND_3_VALIDATED}.
*
* @param keyingMaterial The keying material as returned from {@link #calculateKeyingMaterial()}.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java
index d5df727d..ad2c6aea 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java
@@ -4,15 +4,12 @@ import java.math.BigInteger;
/**
* A pre-computed prime order group for use during a J-PAKE exchange.
- * <p/>
- * <p/>
+ * <p>
* Typically a Schnorr group is used. In general, J-PAKE can use any prime order group
* that is suitable for public key cryptography, including elliptic curve cryptography.
- * <p/>
- * <p/>
+ * <p>
* See {@link JPAKEPrimeOrderGroups} for convenient standard groups.
- * <p/>
- * <p/>
+ * <p>
* NIST <a href="http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/DSA2_All.pdf">publishes</a>
* many groups that can be used for the desired level of security.
*/
@@ -24,12 +21,10 @@ public class JPAKEPrimeOrderGroup
/**
* Constructs a new {@link JPAKEPrimeOrderGroup}.
- * <p/>
- * <p/>
+ * <p>
* In general, you should use one of the pre-approved groups from
* {@link JPAKEPrimeOrderGroups}, rather than manually constructing one.
- * <p/>
- * <p/>
+ * <p>
* The following basic checks are performed:
* <ul>
* <li>p-1 must be evenly divisible by q</li>
@@ -38,12 +33,10 @@ public class JPAKEPrimeOrderGroup
* <li>p must be prime (within reasonably certainty)</li>
* <li>q must be prime (within reasonably certainty)</li>
* </ul>
- * <p/>
- * <p/>
+ * <p>
* The prime checks are performed using {@link BigInteger#isProbablePrime(int)},
* and are therefore subject to the same probability guarantees.
- * <p/>
- * <p/>
+ * <p>
* These checks prevent trivial mistakes.
* However, due to the small uncertainties if p and q are not prime,
* advanced attacks are not prevented.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java
index 812d776e..2fb18613 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroups.java
@@ -5,12 +5,10 @@ import java.math.BigInteger;
/**
* Standard pre-computed prime order groups for use by J-PAKE.
* (J-PAKE can use pre-computed prime order groups, same as DSA and Diffie-Hellman.)
- * <p/>
- * <p/>
+ * <p>
* This class contains some convenient constants for use as input for
* constructing {@link JPAKEParticipant}s.
- * <p/>
- * <p/>
+ * <p>
* The prime order groups below are taken from Sun's JDK JavaDoc (docs/guide/security/CryptoSpec.html#AppB),
* and from the prime order groups
* <a href="http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/DSA2_All.pdf">published by NIST</a>.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java
index b319f9c4..c704040b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound1Payload.java
@@ -6,19 +6,16 @@ import org.bouncycastle.util.Arrays;
/**
* The payload sent/received during the first round of a J-PAKE exchange.
- * <p/>
- * <p/>
+ * <p>
* Each {@link JPAKEParticipant} creates and sends an instance
* of this payload to the other {@link JPAKEParticipant}.
* The payload to send should be created via
* {@link JPAKEParticipant#createRound1PayloadToSend()}.
- * <p/>
- * <p/>
+ * <p>
* Each {@link JPAKEParticipant} must also validate the payload
* received from the other {@link JPAKEParticipant}.
* The received payload should be validated via
* {@link JPAKEParticipant#validateRound1PayloadReceived(JPAKERound1Payload)}.
- * <p/>
*/
public class JPAKERound1Payload
{
@@ -39,15 +36,17 @@ public class JPAKERound1Payload
/**
* The zero knowledge proof for x1.
- * <p/>
+ * <p>
* This is a two element array, containing {g^v, r} for x1.
+ * </p>
*/
private final BigInteger[] knowledgeProofForX1;
/**
* The zero knowledge proof for x2.
- * <p/>
+ * <p>
* This is a two element array, containing {g^v, r} for x2.
+ * </p>
*/
private final BigInteger[] knowledgeProofForX2;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java
index 8800cf5f..af3cb5fc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound2Payload.java
@@ -6,19 +6,16 @@ import org.bouncycastle.util.Arrays;
/**
* The payload sent/received during the second round of a J-PAKE exchange.
- * <p/>
- * <p/>
+ * <p>
* Each {@link JPAKEParticipant} creates and sends an instance
* of this payload to the other {@link JPAKEParticipant}.
* The payload to send should be created via
* {@link JPAKEParticipant#createRound2PayloadToSend()}
- * <p/>
- * <p/>
+ * <p>
* Each {@link JPAKEParticipant} must also validate the payload
* received from the other {@link JPAKEParticipant}.
* The received payload should be validated via
* {@link JPAKEParticipant#validateRound2PayloadReceived(JPAKERound2Payload)}
- * <p/>
*/
public class JPAKERound2Payload
{
@@ -34,8 +31,9 @@ public class JPAKERound2Payload
/**
* The zero knowledge proof for x2 * s.
- * <p/>
+ * <p>
* This is a two element array, containing {g^v, r} for x2 * s.
+ * </p>
*/
private final BigInteger[] knowledgeProofForX2s;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java
index c1255df6..0fe1cba9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKERound3Payload.java
@@ -5,19 +5,16 @@ import java.math.BigInteger;
/**
* The payload sent/received during the optional third round of a J-PAKE exchange,
* which is for explicit key confirmation.
- * <p/>
- * <p/>
+ * <p>
* Each {@link JPAKEParticipant} creates and sends an instance
* of this payload to the other {@link JPAKEParticipant}.
* The payload to send should be created via
* {@link JPAKEParticipant#createRound3PayloadToSend(BigInteger)}
- * <p/>
- * <p/>
+ * <p>
* Each {@link JPAKEParticipant} must also validate the payload
* received from the other {@link JPAKEParticipant}.
* The received payload should be validated via
* {@link JPAKEParticipant#validateRound3PayloadReceived(JPAKERound3Payload, BigInteger)}
- * <p/>
*/
public class JPAKERound3Payload
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java
index 416152e9..0a6c5fe4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java
@@ -14,13 +14,11 @@ import org.bouncycastle.util.Strings;
/**
* Primitives needed for a J-PAKE exchange.
- * <p/>
- * <p/>
+ * <p>
* The recommended way to perform a J-PAKE exchange is by using
* two {@link JPAKEParticipant}s. Internally, those participants
* call these primitive operations in {@link JPAKEUtil}.
- * <p/>
- * <p/>
+ * <p>
* The primitives, however, can be used without a {@link JPAKEParticipant}
* if needed.
*/
@@ -31,8 +29,7 @@ public class JPAKEUtil
/**
* Return a value that can be used as x1 or x3 during round 1.
- * <p/>
- * <p/>
+ * <p>
* The returned value is a random value in the range <tt>[0, q-1]</tt>.
*/
public static BigInteger generateX1(
@@ -46,8 +43,7 @@ public class JPAKEUtil
/**
* Return a value that can be used as x2 or x4 during round 1.
- * <p/>
- * <p/>
+ * <p>
* The returned value is a random value in the range <tt>[1, q-1]</tt>.
*/
public static BigInteger generateX2(
@@ -188,10 +184,9 @@ public class JPAKEUtil
/**
* Validates that ga is not 1.
- * <p/>
- * <p/>
+ * <p>
* As described by Feng Hao...
- * <p/>
+ * <p>
* <blockquote>
* Alice could simply check ga != 1 to ensure it is a generator.
* In fact, as we will explain in Section 3, (x1 + x3 + x4 ) is random over Zq even in the face of active attacks.
@@ -250,8 +245,6 @@ public class JPAKEUtil
* Calculates the keying material, which can be done after round 2 has completed.
* A session key must be derived from this key material using a secure key derivation function (KDF).
* The KDF used to derive the key is handled externally (i.e. not by {@link JPAKEParticipant}).
- * <p/>
- * <p/>
* <pre>
* KeyingMaterial = (B/g^{x2*x4*s})^x2
* </pre>
@@ -326,8 +319,6 @@ public class JPAKEUtil
* Calculates the MacTag (to be used for key confirmation), as defined by
* <a href="http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf">NIST SP 800-56A Revision 1</a>,
* Section 8.2 Unilateral Key Confirmation for Key Agreement Schemes.
- * <p/>
- * <p/>
* <pre>
* MacTag = HMAC(MacKey, MacLen, MacData)
*
@@ -339,10 +330,9 @@ public class JPAKEUtil
* is always the initiator for key confirmation.
*
* HMAC = {@link HMac} used with the given {@link Digest}
- * H = The given {@link Digest}</li>
+ * H = The given {@link Digest}
* MacLen = length of MacTag
* </pre>
- * <p/>
*/
public static BigInteger calculateMacTag(
String participantId,
@@ -383,8 +373,6 @@ public class JPAKEUtil
/**
* Calculates the MacKey (i.e. the key to use when calculating the MagTag for key confirmation).
- * <p/>
- * <p/>
* <pre>
* MacKey = H(K || "JPAKE_KC")
* </pre>
@@ -407,7 +395,6 @@ public class JPAKEUtil
/**
* Validates the MacTag received from the partner participant.
- * <p/>
*
* @param partnerMacTag the MacTag received from the partner.
* @throws CryptoException if the participantId strings are equal.
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
new file mode 100644
index 00000000..df16b4fe
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes for Password Authenticated Key Exchange by Juggling (J-PAKE) key exchange.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKDFParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKDFParameters.java
index ae551dd5..315c5486 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKDFParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKDFParameters.java
@@ -1,7 +1,6 @@
package org.bouncycastle.crypto.agreement.kdf;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.crypto.DerivationParameters;
public class DHKDFParameters
@@ -13,7 +12,7 @@ public class DHKDFParameters
private byte[] extraInfo;
public DHKDFParameters(
- DERObjectIdentifier algorithm,
+ ASN1ObjectIdentifier algorithm,
int keySize,
byte[] z)
{
@@ -21,12 +20,12 @@ public class DHKDFParameters
}
public DHKDFParameters(
- DERObjectIdentifier algorithm,
+ ASN1ObjectIdentifier algorithm,
int keySize,
byte[] z,
byte[] extraInfo)
{
- this.algorithm = new ASN1ObjectIdentifier(algorithm.getId());
+ this.algorithm = algorithm;
this.keySize = keySize;
this.z = z;
this.extraInfo = extraInfo;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java
index 947bc5c4..6feb5076 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/DHKEKGenerator.java
@@ -4,7 +4,7 @@ import java.io.IOException;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
@@ -12,7 +12,7 @@ import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.DerivationFunction;
import org.bouncycastle.crypto.DerivationParameters;
import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Pack;
/**
* RFC 2631 Diffie-hellman KEK derivation function.
@@ -22,7 +22,7 @@ public class DHKEKGenerator
{
private final Digest digest;
- private DERObjectIdentifier algorithm;
+ private ASN1ObjectIdentifier algorithm;
private int keySize;
private byte[] z;
private byte[] partyAInfo;
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 500b1dd0..5d15b992 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
@@ -16,7 +16,7 @@ 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;
+import org.bouncycastle.util.Pack;
/**
* X9.63 based key derivation function for ECDH CMS.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/package.html
new file mode 100644
index 00000000..a00160f3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes for KDF based key derivation functions.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html
new file mode 100644
index 00000000..4b49331c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Basic key agreement classes.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java
index 4df90236..436a94c1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Client.java
@@ -5,6 +5,7 @@ import java.security.SecureRandom;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
/**
* Implements the client side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
@@ -25,6 +26,10 @@ public class SRP6Client
protected BigInteger u;
protected BigInteger S;
+ protected BigInteger M1;
+ protected BigInteger M2;
+ protected BigInteger Key;
+
protected Digest digest;
protected SecureRandom random;
@@ -47,6 +52,11 @@ public class SRP6Client
this.random = random;
}
+ public void init(SRP6GroupParameters group, Digest digest, SecureRandom random)
+ {
+ init(group.getN(), group.getG(), digest, random);
+ }
+
/**
* Generates client's credentials given the client's salt, identity and password
* @param salt The salt used in the client's verifier.
@@ -64,7 +74,7 @@ public class SRP6Client
}
/**
- * Generates client's verification message given the server's credentials
+ * Generates the secret S given the server's credentials
* @param serverB The server's credentials
* @return Client's verification message for the server
* @throws CryptoException If server's credentials are invalid
@@ -90,4 +100,57 @@ public class SRP6Client
BigInteger tmp = g.modPow(x, N).multiply(k).mod(N);
return B.subtract(tmp).mod(N).modPow(exp, N);
}
+
+ /**
+ * Computes the client evidence message M1 using the previously received values.
+ * To be called after calculating the secret S.
+ * @return M1: the client side generated evidence message
+ * @throws CryptoException
+ */
+ public BigInteger calculateClientEvidenceMessage() throws CryptoException{
+ // verify pre-requirements
+ if ((this.A==null)||(this.B==null)||(this.S==null)){
+ throw new CryptoException("Impossible to compute M1: " +
+ "some data are missing from the previous operations (A,B,S)");
+ }
+ // compute the client evidence message 'M1'
+ this.M1 = SRP6Util.calculateM1(digest, N, A, B, S);
+ return M1;
+ }
+
+ /** Authenticates the server evidence message M2 received and saves it only if correct.
+ * @param M2: the server side generated evidence message
+ * @return A boolean indicating if the server message M2 was the expected one.
+ * @throws CryptoException
+ */
+ public boolean verifyServerEvidenceMessage(BigInteger serverM2) throws CryptoException{
+ //verify pre-requirements
+ if ((this.A==null)||(this.M1==null)||(this.S==null)){
+ throw new CryptoException("Impossible to compute and verify M2: " +
+ "some data are missing from the previous operations (A,M1,S)");
+ }
+ // Compute the own server evidence message 'M2'
+ BigInteger computedM2 = SRP6Util.calculateM2(digest, N, A, M1, S);
+ if (computedM2.equals(serverM2)){
+ this.M2 = serverM2;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Computes the final session key as a result of the SRP successful mutual authentication
+ * To be called after verifying the server evidence message M2.
+ * @return Key: the mutually authenticated symmetric session key
+ * @throws CryptoException
+ */
+ public BigInteger calculateSessionKey() throws CryptoException{
+ //verify pre-requirements (here we enforce a previous calculation of M1 and M2)
+ if ((this.S==null)||(this.M1==null)||(this.M2==null)){
+ throw new CryptoException("Impossible to compute Key: " +
+ "some data are missing from the previous operations (S,M1,M2)");
+ }
+ this.Key = SRP6Util.calculateKey(digest, N, S);
+ return Key;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java
index fb208388..c0cc7bf1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Server.java
@@ -5,6 +5,7 @@ import java.security.SecureRandom;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
/**
* Implements the server side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
@@ -27,7 +28,10 @@ public class SRP6Server
protected BigInteger u;
protected BigInteger S;
-
+ protected BigInteger M1;
+ protected BigInteger M2;
+ protected BigInteger Key;
+
public SRP6Server()
{
}
@@ -50,6 +54,11 @@ public class SRP6Server
this.digest = digest;
}
+ public void init(SRP6GroupParameters group, BigInteger v, Digest digest, SecureRandom random)
+ {
+ init(group.getN(), group.getG(), v, digest, random);
+ }
+
/**
* Generates the server's credentials that are to be sent to the client.
* @return The server's public value to the client
@@ -87,4 +96,59 @@ public class SRP6Server
{
return v.modPow(u, N).multiply(A).mod(N).modPow(b, N);
}
+
+ /**
+ * Authenticates the received client evidence message M1 and saves it only if correct.
+ * To be called after calculating the secret S.
+ * @param M1: the client side generated evidence message
+ * @return A boolean indicating if the client message M1 was the expected one.
+ * @throws CryptoException
+ */
+ public boolean verifyClientEvidenceMessage(BigInteger clientM1) throws CryptoException{
+ //verify pre-requirements
+ if ((this.A==null)||(this.B==null)||(this.S==null)){
+ throw new CryptoException("Impossible to compute and verify M1: " +
+ "some data are missing from the previous operations (A,B,S)");
+ }
+ // Compute the own client evidence message 'M1'
+ BigInteger computedM1 = SRP6Util.calculateM1(digest, N, A, B, S);
+ if (computedM1.equals(clientM1)){
+ this.M1 = clientM1;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Computes the server evidence message M2 using the previously verified values.
+ * To be called after successfully verifying the client evidence message M1.
+ * @return M2: the server side generated evidence message
+ * @throws CryptoException
+ */
+ public BigInteger calculateServerEvidenceMessage() throws CryptoException{
+ //verify pre-requirements
+ if ((this.A==null)||(this.M1==null)||(this.S==null)){
+ throw new CryptoException("Impossible to compute M2: " +
+ "some data are missing from the previous operations (A,M1,S)");
+ }
+ // Compute the server evidence message 'M2'
+ this.M2 = SRP6Util.calculateM2(digest, N, A, M1, S);
+ return M2;
+ }
+
+ /**
+ * Computes the final session key as a result of the SRP successful mutual authentication
+ * To be called after calculating the server evidence message M2.
+ * @return Key: the mutual authenticated symmetric session key
+ * @throws CryptoException
+ */
+ public BigInteger calculateSessionKey() throws CryptoException{
+ //verify pre-requirements
+ if ((this.S==null)||(this.M1==null)||(this.M2==null)){
+ throw new CryptoException("Impossible to compute Key: " +
+ "some data are missing from the previous operations (S,M1,M2)");
+ }
+ this.Key = SRP6Util.calculateKey(digest, N, S);
+ return Key;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6StandardGroups.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6StandardGroups.java
new file mode 100644
index 00000000..cc8b3564
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6StandardGroups.java
@@ -0,0 +1,157 @@
+package org.bouncycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
+import org.bouncycastle.util.encoders.Hex;
+
+public class SRP6StandardGroups
+{
+ private static BigInteger fromHex(String hex)
+ {
+ return new BigInteger(1, Hex.decode(hex));
+ }
+
+ private static SRP6GroupParameters fromNG(String hexN, String hexG)
+ {
+ return new SRP6GroupParameters(fromHex(hexN), fromHex(hexG));
+ }
+
+ /*
+ * RFC 5054
+ */
+ private static final String rfc5054_1024_N = "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C"
+ + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4"
+ + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29"
+ + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A" + "FD5138FE8376435B9FC61D2FC0EB06E3";
+ private static final String rfc5054_1024_g = "02";
+ public static final SRP6GroupParameters rfc5054_1024 = fromNG(rfc5054_1024_N, rfc5054_1024_g);
+
+ private static final String rfc5054_1536_N = "9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA961"
+ + "4B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F843"
+ + "80B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0B"
+ + "E3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF5"
+ + "6EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734A"
+ + "F7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E"
+ + "8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB";
+ private static final String rfc5054_1536_g = "02";
+ public static final SRP6GroupParameters rfc5054_1536 = fromNG(rfc5054_1536_N, rfc5054_1536_g);
+
+ private static final String rfc5054_2048_N = "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC319294"
+ + "3DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310D"
+ + "CD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FB"
+ + "D5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF74"
+ + "7359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A"
+ + "436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D"
+ + "5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E73"
+ + "03CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB6"
+ + "94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F" + "9E4AFF73";
+ private static final String rfc5054_2048_g = "02";
+ public static final SRP6GroupParameters rfc5054_2048 = fromNG(rfc5054_2048_N, rfc5054_2048_g);
+
+ private static final String rfc5054_3072_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
+ private static final String rfc5054_3072_g = "05";
+ public static final SRP6GroupParameters rfc5054_3072 = fromNG(rfc5054_3072_N, rfc5054_3072_g);
+
+ private static final String rfc5054_4096_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + "FFFFFFFFFFFFFFFF";
+ private static final String rfc5054_4096_g = "05";
+ public static final SRP6GroupParameters rfc5054_4096 = fromNG(rfc5054_4096_N, rfc5054_4096_g);
+
+ private static final String rfc5054_6144_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF";
+ private static final String rfc5054_6144_g = "05";
+ public static final SRP6GroupParameters rfc5054_6144 = fromNG(rfc5054_6144_N, rfc5054_6144_g);
+
+ private static final String rfc5054_8192_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ + "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
+ + "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
+ + "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
+ + "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
+ + "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
+ + "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
+ + "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
+ + "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF";
+ private static final String rfc5054_8192_g = "13";
+ public static final SRP6GroupParameters rfc5054_8192 = fromNG(rfc5054_8192_N, rfc5054_8192_g);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java
index ad5ceac8..6bcf0183 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6Util.java
@@ -60,6 +60,69 @@ public class SRP6Util
return val;
}
+ /**
+ * Computes the client evidence message (M1) according to the standard routine:
+ * M1 = H( A | B | S )
+ * @param digest The Digest used as the hashing function H
+ * @param N Modulus used to get the pad length
+ * @param A The public client value
+ * @param B The public server value
+ * @param S The secret calculated by both sides
+ * @return M1 The calculated client evidence message
+ */
+ public static BigInteger calculateM1(Digest digest, BigInteger N, BigInteger A, BigInteger B, BigInteger S) {
+ BigInteger M1 = hashPaddedTriplet(digest,N,A,B,S);
+ return M1;
+ }
+
+ /**
+ * Computes the server evidence message (M2) according to the standard routine:
+ * M2 = H( A | M1 | S )
+ * @param digest The Digest used as the hashing function H
+ * @param N Modulus used to get the pad length
+ * @param A The public client value
+ * @param M1 The client evidence message
+ * @param S The secret calculated by both sides
+ * @return M2 The calculated server evidence message
+ */
+ public static BigInteger calculateM2(Digest digest, BigInteger N, BigInteger A, BigInteger M1, BigInteger S){
+ BigInteger M2 = hashPaddedTriplet(digest,N,A,M1,S);
+ return M2;
+ }
+
+ /**
+ * Computes the final Key according to the standard routine: Key = H(S)
+ * @param digest The Digest used as the hashing function H
+ * @param N Modulus used to get the pad length
+ * @param S The secret calculated by both sides
+ * @return
+ */
+ public static BigInteger calculateKey(Digest digest, BigInteger N, BigInteger S) {
+ int padLength = (N.bitLength() + 7) / 8;
+ byte[] _S = getPadded(S,padLength);
+ digest.update(_S, 0, _S.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+ return new BigInteger(1, output);
+ }
+
+ private static BigInteger hashPaddedTriplet(Digest digest, BigInteger N, BigInteger n1, BigInteger n2, BigInteger n3){
+ int padLength = (N.bitLength() + 7) / 8;
+
+ byte[] n1_bytes = getPadded(n1, padLength);
+ byte[] n2_bytes = getPadded(n2, padLength);
+ byte[] n3_bytes = getPadded(n3, padLength);
+
+ digest.update(n1_bytes, 0, n1_bytes.length);
+ digest.update(n2_bytes, 0, n2_bytes.length);
+ digest.update(n3_bytes, 0, n3_bytes.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ return new BigInteger(1, output);
+ }
private static BigInteger hashPaddedPair(Digest digest, BigInteger N, BigInteger n1, BigInteger n2)
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java
index 631ecc6e..e0ae2004 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/SRP6VerifierGenerator.java
@@ -3,6 +3,7 @@ package org.bouncycastle.crypto.agreement.srp;
import java.math.BigInteger;
import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
/**
* Generates new SRP verifier for user
@@ -31,6 +32,13 @@ public class SRP6VerifierGenerator
this.digest = digest;
}
+ public void init(SRP6GroupParameters group, Digest digest)
+ {
+ this.N = group.getN();
+ this.g = group.getG();
+ this.digest = digest;
+ }
+
/**
* Creates a new SRP verifier
* @param salt The salt to use, generally should be large and random
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/package.html
new file mode 100644
index 00000000..c125ffe2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/srp/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Support classes for Secure Remote Password (SRP) protocol.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/AllTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/AllTests.java
new file mode 100644
index 00000000..cb5a862b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/AllTests.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.crypto.agreement.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class AllTests
+ extends TestCase
+{
+ public static void main(String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("JPKAE Engine Tests");
+
+ suite.addTestSuite(JPAKEParticipantTest.class);
+ suite.addTestSuite(JPAKEPrimeOrderGroupTest.class);
+ suite.addTestSuite(JPAKEUtilTest.class);
+
+ return suite;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEParticipantTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEParticipantTest.java
new file mode 100644
index 00000000..c6ecba22
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEParticipantTest.java
@@ -0,0 +1,561 @@
+package org.bouncycastle.crypto.agreement.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEParticipant;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroup;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroups;
+import org.bouncycastle.crypto.agreement.jpake.JPAKERound1Payload;
+import org.bouncycastle.crypto.agreement.jpake.JPAKERound2Payload;
+import org.bouncycastle.crypto.agreement.jpake.JPAKERound3Payload;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEUtil;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+
+public class JPAKEParticipantTest
+ extends TestCase
+{
+
+ public void testConstruction()
+ throws CryptoException
+ {
+ JPAKEPrimeOrderGroup group = JPAKEPrimeOrderGroups.SUN_JCE_1024;
+ SecureRandom random = new SecureRandom();
+ Digest digest = new SHA256Digest();
+ String participantId = "participantId";
+ char[] password = "password".toCharArray();
+
+ // should succeed
+ new JPAKEParticipant(participantId, password, group, digest, random);
+
+ // null participantId
+ try
+ {
+ new JPAKEParticipant(null, password, group, digest, random);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ // null password
+ try
+ {
+ new JPAKEParticipant(participantId, null, group, digest, random);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ // empty password
+ try
+ {
+ new JPAKEParticipant(participantId, "".toCharArray(), group, digest, random);
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // null group
+ try
+ {
+ new JPAKEParticipant(participantId, password, null, digest, random);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ // null digest
+ try
+ {
+ new JPAKEParticipant(participantId, password, group, null, random);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ // null random
+ try
+ {
+ new JPAKEParticipant(participantId, password, group, digest, null);
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+ }
+
+ public void testSuccessfulExchange()
+ throws CryptoException
+ {
+
+ JPAKEParticipant alice = createAlice();
+ JPAKEParticipant bob = createBob();
+
+ ExchangeAfterRound2Creation exchange = runExchangeUntilRound2Creation(alice, bob);
+
+ alice.validateRound2PayloadReceived(exchange.bobRound2Payload);
+ bob.validateRound2PayloadReceived(exchange.aliceRound2Payload);
+
+ BigInteger aliceKeyingMaterial = alice.calculateKeyingMaterial();
+ BigInteger bobKeyingMaterial = bob.calculateKeyingMaterial();
+
+ JPAKERound3Payload aliceRound3Payload = alice.createRound3PayloadToSend(aliceKeyingMaterial);
+ JPAKERound3Payload bobRound3Payload = bob.createRound3PayloadToSend(bobKeyingMaterial);
+
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+ bob.validateRound3PayloadReceived(aliceRound3Payload, bobKeyingMaterial);
+
+ assertEquals(aliceKeyingMaterial, bobKeyingMaterial);
+
+ }
+
+ public void testIncorrectPassword()
+ throws CryptoException
+ {
+
+ JPAKEParticipant alice = createAlice();
+ JPAKEParticipant bob = createBobWithWrongPassword();
+
+ ExchangeAfterRound2Creation exchange = runExchangeUntilRound2Creation(alice, bob);
+
+ alice.validateRound2PayloadReceived(exchange.bobRound2Payload);
+ bob.validateRound2PayloadReceived(exchange.aliceRound2Payload);
+
+ BigInteger aliceKeyingMaterial = alice.calculateKeyingMaterial();
+ BigInteger bobKeyingMaterial = bob.calculateKeyingMaterial();
+
+ JPAKERound3Payload aliceRound3Payload = alice.createRound3PayloadToSend(aliceKeyingMaterial);
+ JPAKERound3Payload bobRound3Payload = bob.createRound3PayloadToSend(bobKeyingMaterial);
+
+ try
+ {
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ try
+ {
+ bob.validateRound3PayloadReceived(aliceRound3Payload, bobKeyingMaterial);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ }
+
+ /**
+ * Tests that {@link JPAKEParticipant} throws appropriate {@link IllegalStateException}s
+ * when the methods are called in the wrong order.
+ */
+ public void testStateValidation()
+ throws CryptoException
+ {
+
+ JPAKEParticipant alice = createAlice();
+ JPAKEParticipant bob = createBob();
+
+ // We're testing alice here. Bob is just used for help.
+
+ // START ROUND 1 CHECKS
+
+ assertEquals(JPAKEParticipant.STATE_INITIALIZED, alice.getState());
+
+ // create round 2 before round 1
+ try
+ {
+ alice.createRound2PayloadToSend();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ JPAKERound1Payload aliceRound1Payload = alice.createRound1PayloadToSend();
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_1_CREATED, alice.getState());
+
+ // create round 1 payload twice
+ try
+ {
+ alice.createRound1PayloadToSend();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // create round 2 before validating round 1
+ try
+ {
+ alice.createRound2PayloadToSend();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // validate round 2 before validating round 1
+ try
+ {
+ alice.validateRound2PayloadReceived(null);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ JPAKERound1Payload bobRound1Payload = bob.createRound1PayloadToSend();
+
+ alice.validateRound1PayloadReceived(bobRound1Payload);
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_1_VALIDATED, alice.getState());
+
+ // validate round 1 payload twice
+ try
+ {
+ alice.validateRound1PayloadReceived(bobRound1Payload);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ bob.validateRound1PayloadReceived(aliceRound1Payload);
+
+ // START ROUND 2 CHECKS
+
+ JPAKERound2Payload aliceRound2Payload = alice.createRound2PayloadToSend();
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_2_CREATED, alice.getState());
+
+ // create round 2 payload twice
+ try
+ {
+ alice.createRound2PayloadToSend();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // create key before validating round 2
+ try
+ {
+ alice.calculateKeyingMaterial();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // validate round 3 before validating round 2
+ try
+ {
+ alice.validateRound3PayloadReceived(null, null);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ JPAKERound2Payload bobRound2Payload = bob.createRound2PayloadToSend();
+
+ alice.validateRound2PayloadReceived(bobRound2Payload);
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_2_VALIDATED, alice.getState());
+
+ // validate round 2 payload twice
+ try
+ {
+ alice.validateRound2PayloadReceived(bobRound2Payload);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ bob.validateRound2PayloadReceived(aliceRound2Payload);
+
+ // create round 3 before calculating key
+ try
+ {
+ alice.createRound3PayloadToSend(BigInteger.ONE);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ // START KEY CALCULATION CHECKS
+
+ BigInteger aliceKeyingMaterial = alice.calculateKeyingMaterial();
+
+ assertEquals(JPAKEParticipant.STATE_KEY_CALCULATED, alice.getState());
+
+ // calculate key twice
+ try
+ {
+ alice.calculateKeyingMaterial();
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ BigInteger bobKeyingMaterial = bob.calculateKeyingMaterial();
+
+ // START ROUND 3 CHECKS
+
+ JPAKERound3Payload aliceRound3Payload = alice.createRound3PayloadToSend(aliceKeyingMaterial);
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_3_CREATED, alice.getState());
+
+ // create round 3 payload twice
+ try
+ {
+ alice.createRound3PayloadToSend(aliceKeyingMaterial);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ JPAKERound3Payload bobRound3Payload = bob.createRound3PayloadToSend(bobKeyingMaterial);
+
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+
+ assertEquals(JPAKEParticipant.STATE_ROUND_3_VALIDATED, alice.getState());
+
+ // validate round 3 payload twice
+ try
+ {
+ alice.validateRound3PayloadReceived(bobRound3Payload, aliceKeyingMaterial);
+ fail();
+ }
+ catch (IllegalStateException e)
+ {
+ // pass
+ }
+
+ bob.validateRound3PayloadReceived(aliceRound3Payload, bobKeyingMaterial);
+
+
+ }
+
+ /**
+ * Tests that {@link JPAKEParticipant#validateRound1PayloadReceived(JPAKERound1Payload)}
+ * calls the appropriate validate methods in {@link JPAKEUtil}.
+ * Note that {@link JPAKEUtilTest} tests the individual validate methods
+ * called by {@link JPAKEParticipant} more extensively.
+ */
+ public void testValidateRound1PayloadReceived()
+ throws CryptoException
+ {
+
+ // We're testing alice here. Bob is just used for help.
+
+ JPAKERound1Payload bobRound1Payload = createBob().createRound1PayloadToSend();
+
+ // should succeed
+ createAlice().validateRound1PayloadReceived(bobRound1Payload);
+
+ // alice verifies alice's payload
+ try
+ {
+ JPAKEParticipant alice = createAlice();
+ alice.validateRound1PayloadReceived(alice.createRound1PayloadToSend());
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // g^x4 == 1
+ try
+ {
+ createAlice().validateRound1PayloadReceived(new JPAKERound1Payload(
+ bobRound1Payload.getParticipantId(),
+ bobRound1Payload.getGx1(),
+ BigInteger.ONE,
+ bobRound1Payload.getKnowledgeProofForX1(),
+ bobRound1Payload.getKnowledgeProofForX2()));
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // zero knowledge proof for x3 fails
+ try
+ {
+ JPAKERound1Payload bobRound1Payload2 = createBob().createRound1PayloadToSend();
+ createAlice().validateRound1PayloadReceived(new JPAKERound1Payload(
+ bobRound1Payload.getParticipantId(),
+ bobRound1Payload.getGx1(),
+ bobRound1Payload.getGx2(),
+ bobRound1Payload2.getKnowledgeProofForX1(),
+ bobRound1Payload.getKnowledgeProofForX2()));
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // zero knowledge proof for x4 fails
+ try
+ {
+ JPAKERound1Payload bobRound1Payload2 = createBob().createRound1PayloadToSend();
+ createAlice().validateRound1PayloadReceived(new JPAKERound1Payload(
+ bobRound1Payload.getParticipantId(),
+ bobRound1Payload.getGx1(),
+ bobRound1Payload.getGx2(),
+ bobRound1Payload.getKnowledgeProofForX1(),
+ bobRound1Payload2.getKnowledgeProofForX2()));
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ }
+
+ /**
+ * Tests that {@link JPAKEParticipant#validateRound2PayloadReceived(JPAKERound2Payload)}
+ * calls the appropriate validate methods in {@link JPAKEUtil}.
+ * Note that {@link JPAKEUtilTest} tests the individual validate methods
+ * called by {@link JPAKEParticipant} more extensively.
+ */
+ public void testValidateRound2PayloadReceived()
+ throws CryptoException
+ {
+
+ // We're testing alice here. Bob is just used for help.
+
+ // should succeed
+ ExchangeAfterRound2Creation exchange1 = runExchangeUntilRound2Creation(createAlice(), createBob());
+ exchange1.alice.validateRound2PayloadReceived(exchange1.bobRound2Payload);
+
+ // alice verifies alice's payload
+ ExchangeAfterRound2Creation exchange2 = runExchangeUntilRound2Creation(createAlice(), createBob());
+ try
+ {
+ exchange2.alice.validateRound2PayloadReceived(exchange2.aliceRound2Payload);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong z
+ ExchangeAfterRound2Creation exchange3 = runExchangeUntilRound2Creation(createAlice(), createBob());
+ ExchangeAfterRound2Creation exchange4 = runExchangeUntilRound2Creation(createAlice(), createBob());
+ try
+ {
+ exchange3.alice.validateRound2PayloadReceived(exchange4.bobRound2Payload);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ }
+
+ private static class ExchangeAfterRound2Creation
+ {
+
+ public JPAKEParticipant alice;
+ public JPAKERound2Payload aliceRound2Payload;
+ public JPAKERound2Payload bobRound2Payload;
+
+ public ExchangeAfterRound2Creation(
+ JPAKEParticipant alice,
+ JPAKERound2Payload aliceRound2Payload,
+ JPAKERound2Payload bobRound2Payload)
+ {
+ this.alice = alice;
+ this.aliceRound2Payload = aliceRound2Payload;
+ this.bobRound2Payload = bobRound2Payload;
+ }
+
+ }
+
+ private ExchangeAfterRound2Creation runExchangeUntilRound2Creation(JPAKEParticipant alice, JPAKEParticipant bob)
+ throws CryptoException
+ {
+ JPAKERound1Payload aliceRound1Payload = alice.createRound1PayloadToSend();
+ JPAKERound1Payload bobRound1Payload = bob.createRound1PayloadToSend();
+
+ alice.validateRound1PayloadReceived(bobRound1Payload);
+ bob.validateRound1PayloadReceived(aliceRound1Payload);
+
+ JPAKERound2Payload aliceRound2Payload = alice.createRound2PayloadToSend();
+ JPAKERound2Payload bobRound2Payload = bob.createRound2PayloadToSend();
+
+ return new ExchangeAfterRound2Creation(
+ alice,
+ aliceRound2Payload,
+ bobRound2Payload);
+ }
+
+ private JPAKEParticipant createAlice()
+ {
+ return createParticipant("alice", "password");
+ }
+
+ private JPAKEParticipant createBob()
+ {
+ return createParticipant("bob", "password");
+ }
+
+ private JPAKEParticipant createBobWithWrongPassword()
+ {
+ return createParticipant("bob", "wrong");
+ }
+
+ private JPAKEParticipant createParticipant(String participantId, String password)
+ {
+ return new JPAKEParticipant(
+ participantId,
+ password.toCharArray(),
+ JPAKEPrimeOrderGroups.SUN_JCE_1024);
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEPrimeOrderGroupTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEPrimeOrderGroupTest.java
new file mode 100644
index 00000000..7d22f16a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEPrimeOrderGroupTest.java
@@ -0,0 +1,85 @@
+package org.bouncycastle.crypto.agreement.test;
+
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroup;
+
+public class JPAKEPrimeOrderGroupTest
+ extends TestCase
+{
+
+ public void testConstruction()
+ throws CryptoException
+ {
+ // p-1 not evenly divisible by q
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(7), BigInteger.valueOf(5), BigInteger.valueOf(6));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // g < 2
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(11), BigInteger.valueOf(5), BigInteger.valueOf(1));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // g > p-1
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(11), BigInteger.valueOf(5), BigInteger.valueOf(11));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // g^q mod p not equal 1
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(11), BigInteger.valueOf(5), BigInteger.valueOf(6));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // p not prime
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(15), BigInteger.valueOf(2), BigInteger.valueOf(4));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // q not prime
+ try
+ {
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(7), BigInteger.valueOf(6), BigInteger.valueOf(3));
+ fail();
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ // should succeed
+ new JPAKEPrimeOrderGroup(BigInteger.valueOf(7), BigInteger.valueOf(3), BigInteger.valueOf(4));
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEUtilTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEUtilTest.java
new file mode 100644
index 00000000..20268b8d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/test/JPAKEUtilTest.java
@@ -0,0 +1,267 @@
+package org.bouncycastle.crypto.agreement.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroup;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEPrimeOrderGroups;
+import org.bouncycastle.crypto.agreement.jpake.JPAKEUtil;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+
+public class JPAKEUtilTest
+ extends TestCase
+{
+ private static final BigInteger TEN = BigInteger.valueOf(10);
+
+ public void testValidateGx4()
+ throws CryptoException
+ {
+ JPAKEUtil.validateGx4(TEN);
+
+ try
+ {
+ JPAKEUtil.validateGx4(BigInteger.ONE);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateGa()
+ throws CryptoException
+ {
+ JPAKEUtil.validateGa(TEN);
+
+ try
+ {
+ JPAKEUtil.validateGa(BigInteger.ONE);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateParticipantIdsDiffer()
+ throws CryptoException
+ {
+ JPAKEUtil.validateParticipantIdsDiffer("a", "b");
+ JPAKEUtil.validateParticipantIdsDiffer("a", "A");
+
+ try
+ {
+ JPAKEUtil.validateParticipantIdsDiffer("a", "a");
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateParticipantIdsEqual()
+ throws CryptoException
+ {
+ JPAKEUtil.validateParticipantIdsEqual("a", "a");
+
+ try
+ {
+ JPAKEUtil.validateParticipantIdsEqual("a", "b");
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateMacTag()
+ throws CryptoException
+ {
+ JPAKEPrimeOrderGroup pg1 = JPAKEPrimeOrderGroups.SUN_JCE_1024;
+
+ SecureRandom random = new SecureRandom();
+ Digest digest = new SHA256Digest();
+
+ BigInteger x1 = JPAKEUtil.generateX1(pg1.getQ(), random);
+ BigInteger x2 = JPAKEUtil.generateX2(pg1.getQ(), random);
+ BigInteger x3 = JPAKEUtil.generateX1(pg1.getQ(), random);
+ BigInteger x4 = JPAKEUtil.generateX2(pg1.getQ(), random);
+
+ BigInteger gx1 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x1);
+ BigInteger gx2 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x2);
+ BigInteger gx3 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x3);
+ BigInteger gx4 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x4);
+
+ BigInteger gB = JPAKEUtil.calculateGA(pg1.getP(), gx3, gx1, gx2);
+
+ BigInteger s = JPAKEUtil.calculateS("password".toCharArray());
+
+ BigInteger xs = JPAKEUtil.calculateX2s(pg1.getQ(), x4, s);
+
+ BigInteger B = JPAKEUtil.calculateA(pg1.getP(), pg1.getQ(), gB, xs);
+
+ BigInteger keyingMaterial = JPAKEUtil.calculateKeyingMaterial(pg1.getP(), pg1.getQ(), gx4, x2, s, B);
+
+ BigInteger macTag = JPAKEUtil.calculateMacTag("participantId", "partnerParticipantId", gx1, gx2, gx3, gx4, keyingMaterial, digest);
+
+ // should succed
+ JPAKEUtil.validateMacTag("partnerParticipantId", "participantId", gx3, gx4, gx1, gx2, keyingMaterial, digest, macTag);
+
+ // validating own macTag (as opposed to the other party's mactag)
+ try
+ {
+ JPAKEUtil.validateMacTag("participantId", "partnerParticipantId", gx1, gx2, gx3, gx4, keyingMaterial, digest, macTag);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // participant ids switched
+ try
+ {
+ JPAKEUtil.validateMacTag("participantId", "partnerParticipantId", gx3, gx4, gx1, gx2, keyingMaterial, digest, macTag);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateNotNull()
+ {
+ JPAKEUtil.validateNotNull("a", "description");
+
+ try
+ {
+ JPAKEUtil.validateNotNull(null, "description");
+ fail();
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+ }
+
+ public void testValidateZeroKnowledgeProof()
+ throws CryptoException
+ {
+ JPAKEPrimeOrderGroup pg1 = JPAKEPrimeOrderGroups.SUN_JCE_1024;
+
+ SecureRandom random = new SecureRandom();
+ Digest digest1 = new SHA256Digest();
+
+ BigInteger x1 = JPAKEUtil.generateX1(pg1.getQ(), random);
+ BigInteger gx1 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x1);
+ String participantId1 = "participant1";
+
+ BigInteger[] zkp1 = JPAKEUtil.calculateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, x1, participantId1, digest1, random);
+
+ // should succeed
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, zkp1, participantId1, digest1);
+
+ // wrong group
+ JPAKEPrimeOrderGroup pg2 = JPAKEPrimeOrderGroups.NIST_3072;
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg2.getP(), pg2.getQ(), pg2.getG(), gx1, zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong digest
+ Digest digest2 = new SHA1Digest();
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, zkp1, participantId1, digest2);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong participant
+ String participantId2 = "participant2";
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, zkp1, participantId2, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong gx
+ BigInteger x2 = JPAKEUtil.generateX1(pg1.getQ(), random);
+ BigInteger gx2 = JPAKEUtil.calculateGx(pg1.getP(), pg1.getG(), x2);
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx2, zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // wrong zkp
+ BigInteger[] zkp2 = JPAKEUtil.calculateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx2, x2, participantId1, digest1, random);
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), gx1, zkp2, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // gx <= 0
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), BigInteger.ZERO, zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // gx >= p
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), pg1.getP(), zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+
+ // gx mod q == 1
+ try
+ {
+ JPAKEUtil.validateZeroKnowledgeProof(pg1.getP(), pg1.getQ(), pg1.getG(), pg1.getQ().add(BigInteger.ONE), zkp1, participantId1, digest1);
+ fail();
+ }
+ catch (CryptoException e)
+ {
+ // pass
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html
new file mode 100644
index 00000000..5bab3fc1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Classes for supporting commitment calculation.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java
new file mode 100644
index 00000000..d79fece8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.digests;
+
+/**
+ * Encodable digests allow you to download an encoded copy of their internal state. This is useful for the situation where
+ * you need to generate a signature on an external device and it allows for "sign with last round", so a copy of the
+ * internal state of the digest, plus the last few blocks of the message are all that needs to be sent, rather than the
+ * entire message.
+ */
+public interface EncodableDigest
+{
+ /**
+ * Return an encoded byte array for the digest's internal state
+ *
+ * @return an encoding of the digests internal state.
+ */
+ byte[] getEncodedState();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java
index 38a52aab..2df2d51a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/GOST3411Digest.java
@@ -5,9 +5,9 @@ import org.bouncycastle.crypto.ExtendedDigest;
import org.bouncycastle.crypto.engines.GOST28147Engine;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithSBox;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
* implementation of GOST R 34.11-94
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
index 15f3ebbd..29692bad 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/GeneralDigest.java
@@ -2,6 +2,7 @@ package org.bouncycastle.crypto.digests;
import org.bouncycastle.crypto.ExtendedDigest;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
* base implementation of MD4 family style digest as outlined in
@@ -11,8 +12,9 @@ public abstract class GeneralDigest
implements ExtendedDigest, Memoable
{
private static final int BYTE_LENGTH = 64;
- private byte[] xBuf;
- private int xBufOff;
+
+ private final byte[] xBuf = new byte[4];
+ private int xBufOff;
private long byteCount;
@@ -21,7 +23,6 @@ public abstract class GeneralDigest
*/
protected GeneralDigest()
{
- xBuf = new byte[4];
xBufOff = 0;
}
@@ -32,11 +33,16 @@ public abstract class GeneralDigest
*/
protected GeneralDigest(GeneralDigest t)
{
- xBuf = new byte[t.xBuf.length];
-
copyIn(t);
}
+ protected GeneralDigest(byte[] encodedState)
+ {
+ System.arraycopy(encodedState, 0, xBuf, 0, xBuf.length);
+ xBufOff = Pack.bigEndianToInt(encodedState, 4);
+ byteCount = Pack.bigEndianToLong(encodedState, 8);
+ }
+
protected void copyIn(GeneralDigest t)
{
System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
@@ -129,6 +135,13 @@ public abstract class GeneralDigest
}
}
+ protected void populateState(byte[] state)
+ {
+ System.arraycopy(xBuf, 0, state, 0, xBufOff);
+ Pack.intToBigEndian(xBufOff, state, 4);
+ Pack.longToBigEndian(byteCount, state, 8);
+ }
+
public int getByteLength()
{
return BYTE_LENGTH;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
index 5c79e4ee..8ea474b3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/LongDigest.java
@@ -1,18 +1,18 @@
package org.bouncycastle.crypto.digests;
import org.bouncycastle.crypto.ExtendedDigest;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
* Base class for SHA-384 and SHA-512.
*/
public abstract class LongDigest
- implements ExtendedDigest, Memoable
+ implements ExtendedDigest, Memoable, EncodableDigest
{
private static final int BYTE_LENGTH = 128;
-
- private byte[] xBuf;
+
+ private byte[] xBuf = new byte[8];
private int xBufOff;
private long byteCount1;
@@ -28,7 +28,6 @@ public abstract class LongDigest
*/
protected LongDigest()
{
- xBuf = new byte[8];
xBufOff = 0;
reset();
@@ -41,8 +40,6 @@ public abstract class LongDigest
*/
protected LongDigest(LongDigest t)
{
- xBuf = new byte[t.xBuf.length];
-
copyIn(t);
}
@@ -67,6 +64,56 @@ public abstract class LongDigest
wOff = t.wOff;
}
+ protected void populateState(byte[] state)
+ {
+ System.arraycopy(xBuf, 0, state, 0, xBufOff);
+ Pack.intToBigEndian(xBufOff, state, 8);
+ Pack.longToBigEndian(byteCount1, state, 12);
+ Pack.longToBigEndian(byteCount2, state, 20);
+ Pack.longToBigEndian(H1, state, 28);
+ Pack.longToBigEndian(H2, state, 36);
+ Pack.longToBigEndian(H3, state, 44);
+ Pack.longToBigEndian(H4, state, 52);
+ Pack.longToBigEndian(H5, state, 60);
+ Pack.longToBigEndian(H6, state, 68);
+ Pack.longToBigEndian(H7, state, 76);
+ Pack.longToBigEndian(H8, state, 84);
+
+ Pack.intToBigEndian(wOff, state, 92);
+ for (int i = 0; i < wOff; i++)
+ {
+ Pack.longToBigEndian(W[i], state, 96 + (i * 8));
+ }
+ }
+
+ protected void restoreState(byte[] encodedState)
+ {
+ xBufOff = Pack.bigEndianToInt(encodedState, 8);
+ System.arraycopy(encodedState, 0, xBuf, 0, xBufOff);
+ byteCount1 = Pack.bigEndianToLong(encodedState, 12);
+ byteCount2 = Pack.bigEndianToLong(encodedState, 20);
+
+ H1 = Pack.bigEndianToLong(encodedState, 28);
+ H2 = Pack.bigEndianToLong(encodedState, 36);
+ H3 = Pack.bigEndianToLong(encodedState, 44);
+ H4 = Pack.bigEndianToLong(encodedState, 52);
+ H5 = Pack.bigEndianToLong(encodedState, 60);
+ H6 = Pack.bigEndianToLong(encodedState, 68);
+ H7 = Pack.bigEndianToLong(encodedState, 76);
+ H8 = Pack.bigEndianToLong(encodedState, 84);
+
+ wOff = Pack.bigEndianToInt(encodedState, 92);
+ for (int i = 0; i < wOff; i++)
+ {
+ W[i] = Pack.bigEndianToLong(encodedState, 96 + (i * 8));
+ }
+ }
+
+ protected int getEncodedStateSize()
+ {
+ return 96 + (wOff * 8);
+ }
+
public void update(
byte in)
{
@@ -165,7 +212,7 @@ public abstract class LongDigest
{
return BYTE_LENGTH;
}
-
+
protected void processWord(
byte[] in,
int inOff)
@@ -228,7 +275,7 @@ public abstract class LongDigest
long g = H7;
long h = H8;
- int t = 0;
+ int t = 0;
for(int i = 0; i < 10; i ++)
{
// t = 8 * i
@@ -271,7 +318,7 @@ public abstract class LongDigest
e += a;
a += Sum0(b) + Maj(b, c, d);
}
-
+
H1 += a;
H2 += b;
H3 += c;
@@ -358,4 +405,5 @@ public abstract class LongDigest
0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL, 0x431d67c49c100d4cL,
0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L
};
+
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
index 21b1024e..450dda46 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA1Digest.java
@@ -1,7 +1,7 @@
package org.bouncycastle.crypto.digests;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
* implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349.
@@ -11,6 +11,7 @@ import org.bouncycastle.util.Memoable;
*/
public class SHA1Digest
extends GeneralDigest
+ implements EncodableDigest
{
private static final int DIGEST_LENGTH = 20;
@@ -38,6 +39,23 @@ public class SHA1Digest
copyIn(t);
}
+ public SHA1Digest(byte[] encodedState)
+ {
+ super(encodedState);
+
+ H1 = Pack.bigEndianToInt(encodedState, 16);
+ H2 = Pack.bigEndianToInt(encodedState, 20);
+ H3 = Pack.bigEndianToInt(encodedState, 24);
+ H4 = Pack.bigEndianToInt(encodedState, 28);
+ H5 = Pack.bigEndianToInt(encodedState, 32);
+
+ xOff = Pack.bigEndianToInt(encodedState, 36);
+ for (int i = 0; i != xOff; i++)
+ {
+ X[i] = Pack.bigEndianToInt(encodedState, 40 + (i * 4));
+ }
+ }
+
private void copyIn(SHA1Digest t)
{
H1 = t.H1;
@@ -302,6 +320,27 @@ public class SHA1Digest
super.copyIn(d);
copyIn(d);
}
+
+ public byte[] getEncodedState()
+ {
+ byte[] state = new byte[40 + xOff * 4];
+
+ super.populateState(state);
+
+ Pack.intToBigEndian(H1, state, 16);
+ Pack.intToBigEndian(H2, state, 20);
+ Pack.intToBigEndian(H3, state, 24);
+ Pack.intToBigEndian(H4, state, 28);
+ Pack.intToBigEndian(H5, state, 32);
+ Pack.intToBigEndian(xOff, state, 36);
+
+ for (int i = 0; i != xOff; i++)
+ {
+ Pack.intToBigEndian(X[i], state, 40 + (i * 4));
+ }
+
+ return state;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
index d430321b..4f2b2842 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA224Digest.java
@@ -1,8 +1,8 @@
package org.bouncycastle.crypto.digests;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
@@ -18,6 +18,7 @@ import org.bouncycastle.util.Memoable;
*/
public class SHA224Digest
extends GeneralDigest
+ implements EncodableDigest
{
private static final int DIGEST_LENGTH = 28;
@@ -62,6 +63,26 @@ public class SHA224Digest
xOff = t.xOff;
}
+ public SHA224Digest(byte[] encodedState)
+ {
+ super(encodedState);
+
+ H1 = Pack.bigEndianToInt(encodedState, 16);
+ H2 = Pack.bigEndianToInt(encodedState, 20);
+ H3 = Pack.bigEndianToInt(encodedState, 24);
+ H4 = Pack.bigEndianToInt(encodedState, 28);
+ H5 = Pack.bigEndianToInt(encodedState, 32);
+ H6 = Pack.bigEndianToInt(encodedState, 36);
+ H7 = Pack.bigEndianToInt(encodedState, 40);
+ H8 = Pack.bigEndianToInt(encodedState, 44);
+
+ xOff = Pack.bigEndianToInt(encodedState, 48);
+ for (int i = 0; i != xOff; i++)
+ {
+ X[i] = Pack.bigEndianToInt(encodedState, 52 + (i * 4));
+ }
+ }
+
public String getAlgorithmName()
{
return "SHA-224";
@@ -307,5 +328,29 @@ public class SHA224Digest
doCopy(d);
}
+
+ public byte[] getEncodedState()
+ {
+ byte[] state = new byte[52 + xOff * 4];
+
+ super.populateState(state);
+
+ Pack.intToBigEndian(H1, state, 16);
+ Pack.intToBigEndian(H2, state, 20);
+ Pack.intToBigEndian(H3, state, 24);
+ Pack.intToBigEndian(H4, state, 28);
+ Pack.intToBigEndian(H5, state, 32);
+ Pack.intToBigEndian(H6, state, 36);
+ Pack.intToBigEndian(H7, state, 40);
+ Pack.intToBigEndian(H8, state, 44);
+ Pack.intToBigEndian(xOff, state, 48);
+
+ for (int i = 0; i != xOff; i++)
+ {
+ Pack.intToBigEndian(X[i], state, 52 + (i * 4));
+ }
+
+ return state;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
index a2ceda3d..600d2343 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA256Digest.java
@@ -1,8 +1,8 @@
package org.bouncycastle.crypto.digests;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
@@ -18,6 +18,7 @@ import org.bouncycastle.util.Memoable;
*/
public class SHA256Digest
extends GeneralDigest
+ implements EncodableDigest
{
private static final int DIGEST_LENGTH = 32;
@@ -62,6 +63,27 @@ public class SHA256Digest
xOff = t.xOff;
}
+ public SHA256Digest(byte[] encodedState)
+ {
+ super(encodedState);
+
+ H1 = Pack.bigEndianToInt(encodedState, 16);
+ H2 = Pack.bigEndianToInt(encodedState, 20);
+ H3 = Pack.bigEndianToInt(encodedState, 24);
+ H4 = Pack.bigEndianToInt(encodedState, 28);
+ H5 = Pack.bigEndianToInt(encodedState, 32);
+ H6 = Pack.bigEndianToInt(encodedState, 36);
+ H7 = Pack.bigEndianToInt(encodedState, 40);
+ H8 = Pack.bigEndianToInt(encodedState, 44);
+
+ xOff = Pack.bigEndianToInt(encodedState, 48);
+ for (int i = 0; i != xOff; i++)
+ {
+ X[i] = Pack.bigEndianToInt(encodedState, 52 + (i * 4));
+ }
+ }
+
+
public String getAlgorithmName()
{
return "SHA-256";
@@ -310,5 +332,29 @@ public class SHA256Digest
copyIn(d);
}
+
+ public byte[] getEncodedState()
+ {
+ byte[] state = new byte[52 + xOff * 4];
+
+ super.populateState(state);
+
+ Pack.intToBigEndian(H1, state, 16);
+ Pack.intToBigEndian(H2, state, 20);
+ Pack.intToBigEndian(H3, state, 24);
+ Pack.intToBigEndian(H4, state, 28);
+ Pack.intToBigEndian(H5, state, 32);
+ Pack.intToBigEndian(H6, state, 36);
+ Pack.intToBigEndian(H7, state, 40);
+ Pack.intToBigEndian(H8, state, 44);
+ Pack.intToBigEndian(xOff, state, 48);
+
+ for (int i = 0; i != xOff; i++)
+ {
+ Pack.intToBigEndian(X[i], state, 52 + (i * 4));
+ }
+
+ return state;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
index 75d195d4..fc9fa1e7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA384Digest.java
@@ -1,7 +1,7 @@
package org.bouncycastle.crypto.digests;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
@@ -36,6 +36,11 @@ public class SHA384Digest
super(t);
}
+ public SHA384Digest(byte[] encodedState)
+ {
+ restoreState(encodedState);
+ }
+
public String getAlgorithmName()
{
return "SHA-384";
@@ -96,4 +101,11 @@ public class SHA384Digest
super.copyIn(d);
}
+
+ public byte[] getEncodedState()
+ {
+ byte[] encoded = new byte[getEncodedStateSize()];
+ super.populateState(encoded);
+ return encoded;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java
index 15eb77ce..e13dc614 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java
@@ -5,7 +5,7 @@ import org.bouncycastle.util.Arrays;
/**
* implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
- * <p/>
+ * <p>
* Following the naming conventions used in the C source code to enable easy review of the implementation.
*/
public class SHA3Digest
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
index 7db63ad2..644bafad 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512Digest.java
@@ -1,7 +1,7 @@
package org.bouncycastle.crypto.digests;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
@@ -36,6 +36,11 @@ public class SHA512Digest
super(t);
}
+ public SHA512Digest(byte[] encodedState)
+ {
+ restoreState(encodedState);
+ }
+
public String getAlgorithmName()
{
return "SHA-512";
@@ -98,5 +103,12 @@ public class SHA512Digest
copyIn(d);
}
+
+ public byte[] getEncodedState()
+ {
+ byte[] encoded = new byte[getEncodedStateSize()];
+ super.populateState(encoded);
+ return encoded;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java
index 46154618..d5848b17 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHA512tDigest.java
@@ -2,6 +2,7 @@ package org.bouncycastle.crypto.digests;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.MemoableResetException;
+import org.bouncycastle.util.Pack;
/**
* FIPS 180-4 implementation of SHA-512/t
@@ -9,7 +10,7 @@ import org.bouncycastle.util.MemoableResetException;
public class SHA512tDigest
extends LongDigest
{
- private final int digestLength;
+ private int digestLength; // non-final due to old flow analyser.
private long H1t, H2t, H3t, H4t, H5t, H6t, H7t, H8t;
@@ -53,6 +54,17 @@ public class SHA512tDigest
reset(t);
}
+ public SHA512tDigest(byte[] encodedState)
+ {
+ this(readDigestLength(encodedState));
+ restoreState(encodedState);
+ }
+
+ private static int readDigestLength(byte[] encodedState)
+ {
+ return Pack.bigEndianToInt(encodedState, encodedState.length - 4);
+ }
+
public String getAlgorithmName()
{
return "SHA-512/" + Integer.toString(digestLength * 8);
@@ -202,4 +214,14 @@ public class SHA512tDigest
this.H7t = t.H7t;
this.H8t = t.H8t;
}
+
+ public byte[] getEncodedState()
+ {
+ final int baseSize = getEncodedStateSize();
+ byte[] encoded = new byte[baseSize + 4];
+ populateState(encoded);
+ Pack.intToBigEndian(digestLength * 8, encoded, baseSize);
+ return encoded;
+ }
+
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
index 55e579ed..5e90add5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
@@ -1,13 +1,13 @@
package org.bouncycastle.crypto.digests;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Pack;
/**
* Implementation of Chinese SM3 digest as described at
* http://tools.ietf.org/html/draft-shen-sm3-hash-00
* and at .... ( Chinese PDF )
- * <p/>
+ * <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.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
index 06eaabd0..ae1dbd62 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
@@ -8,13 +8,12 @@ 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/>
+ * <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/>
+ * <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
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
index bca524e3..b125dbd6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
@@ -14,18 +14,18 @@ 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/>
+ * <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/>
+ * <p>
* Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
* Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
- * <p/>
+ * <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/>
+ * <p>
* Implemented:
* <ul>
* <li>256, 512 and 1024 bit internal states.</li>
@@ -34,7 +34,7 @@ import org.bouncycastle.util.Memoable;
* parameters.</li>
* <li>Arbitrary output size in 1 byte intervals.</li>
* </ul>
- * <p/>
+ * <p>
* Not implemented:
* <ul>
* <li>Sub-byte length input (bit padding).</li>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html
new file mode 100644
index 00000000..0a0d95ce
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Message digest classes.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java
new file mode 100644
index 00000000..c3d4f5bb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/CustomNamedCurves.java
@@ -0,0 +1,320 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECParametersHolder;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.custom.djb.Curve25519;
+import org.bouncycastle.math.ec.custom.sec.SecP192K1Curve;
+import org.bouncycastle.math.ec.custom.sec.SecP192R1Curve;
+import org.bouncycastle.math.ec.custom.sec.SecP224K1Curve;
+import org.bouncycastle.math.ec.custom.sec.SecP224R1Curve;
+import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve;
+import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve;
+import org.bouncycastle.math.ec.custom.sec.SecP384R1Curve;
+import org.bouncycastle.math.ec.custom.sec.SecP521R1Curve;
+import org.bouncycastle.math.ec.endo.GLVTypeBEndomorphism;
+import org.bouncycastle.math.ec.endo.GLVTypeBParameters;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+
+public class CustomNamedCurves
+{
+ private static ECCurve configureCurve(ECCurve curve)
+ {
+ return curve;
+ }
+
+ private static ECCurve configureCurveGLV(ECCurve c, GLVTypeBParameters p)
+ {
+ return c.configure().setEndomorphism(new GLVTypeBEndomorphism(c, p)).create();
+ }
+
+ /*
+ * curve25519
+ */
+ static X9ECParametersHolder curve25519 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = null;
+ ECCurve curve = configureCurve(new Curve25519());
+
+ /*
+ * NOTE: Curve25519 was specified in Montgomery form. Rewriting in Weierstrass form
+ * involves substitution of variables, so the base-point x coordinate is 9 + (486662 / 3).
+ *
+ * The Curve25519 paper doesn't say which of the two possible y values the base
+ * point has. The choice here is guided by language in the Ed25519 paper.
+ *
+ * (The other possible y value is 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14)
+ */
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A"
+ + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9"));
+
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ /*
+ * secp192k1
+ */
+ static X9ECParametersHolder secp192k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = null;
+ GLVTypeBParameters glv = new GLVTypeBParameters(
+ new BigInteger("bb85691939b869c1d087f601554b96b80cb4f55b35f433c2", 16),
+ new BigInteger("3d84f26c12238d7b4f3d516613c1759033b1a5800175d0b1", 16),
+ new BigInteger[]{
+ new BigInteger("71169be7330b3038edb025f1", 16),
+ new BigInteger("-b3fb3400dec5c4adceb8655c", 16) },
+ new BigInteger[]{
+ new BigInteger("12511cfe811d0f4e6bc688b4d", 16),
+ new BigInteger("71169be7330b3038edb025f1", 16) },
+ new BigInteger("71169be7330b3038edb025f1d0f9", 16),
+ new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16),
+ 208);
+ ECCurve curve = configureCurveGLV(new SecP192K1Curve(), glv);
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"
+ + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D"));
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ /*
+ * secp192r1
+ */
+ static X9ECParametersHolder secp192r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = Hex.decode("3045AE6FC8422F64ED579528D38120EAE12196D5");
+ ECCurve curve = configureCurve(new SecP192R1Curve());
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012"
+ + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811"));
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ /*
+ * secp224k1
+ */
+ static X9ECParametersHolder secp224k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = null;
+ GLVTypeBParameters glv = new GLVTypeBParameters(
+ new BigInteger("fe0e87005b4e83761908c5131d552a850b3f58b749c37cf5b84d6768", 16),
+ new BigInteger("60dcd2104c4cbc0be6eeefc2bdd610739ec34e317f9b33046c9e4788", 16),
+ new BigInteger[]{
+ new BigInteger("6b8cf07d4ca75c88957d9d670591", 16),
+ new BigInteger("-b8adf1378a6eb73409fa6c9c637d", 16) },
+ new BigInteger[]{
+ new BigInteger("1243ae1b4d71613bc9f780a03690e", 16),
+ new BigInteger("6b8cf07d4ca75c88957d9d670591", 16) },
+ new BigInteger("6b8cf07d4ca75c88957d9d67059037a4", 16),
+ new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16),
+ 240);
+ ECCurve curve = configureCurveGLV(new SecP224K1Curve(), glv);
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C"
+ + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5"));
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ /*
+ * secp224r1
+ */
+ static X9ECParametersHolder secp224r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = Hex.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5");
+ ECCurve curve = configureCurve(new SecP224R1Curve());
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21"
+ + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34"));
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ /*
+ * secp256k1
+ */
+ static X9ECParametersHolder secp256k1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = null;
+ GLVTypeBParameters glv = new GLVTypeBParameters(
+ new BigInteger("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee", 16),
+ new BigInteger("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72", 16),
+ new BigInteger[]{
+ new BigInteger("3086d221a7d46bcde86c90e49284eb15", 16),
+ new BigInteger("-e4437ed6010e88286f547fa90abfe4c3", 16) },
+ new BigInteger[]{
+ new BigInteger("114ca50f7a8e2f3f657c1108d9d44cfd8", 16),
+ new BigInteger("3086d221a7d46bcde86c90e49284eb15", 16) },
+ new BigInteger("3086d221a7d46bcde86c90e49284eb153dab", 16),
+ new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16),
+ 272);
+ ECCurve curve = configureCurveGLV(new SecP256K1Curve(), glv);
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
+ + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"));
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ /*
+ * secp256r1
+ */
+ static X9ECParametersHolder secp256r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = Hex.decode("C49D360886E704936A6678E1139D26B7819F7E90");
+ ECCurve curve = configureCurve(new SecP256R1Curve());
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"
+ + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"));
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ /*
+ * secp384r1
+ */
+ static X9ECParametersHolder secp384r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = Hex.decode("A335926AA319A27A1D00896A6773A4827ACDAC73");
+ ECCurve curve = configureCurve(new SecP384R1Curve());
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
+ + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F"));
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ /*
+ * secp521r1
+ */
+ static X9ECParametersHolder secp521r1 = new X9ECParametersHolder()
+ {
+ protected X9ECParameters createParameters()
+ {
+ byte[] S = Hex.decode("D09E8800291CB85396CC6717393284AAA0DA64BA");
+ ECCurve curve = configureCurve(new SecP521R1Curve());
+ ECPoint G = curve.decodePoint(Hex.decode("04"
+ + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
+ + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650"));
+ return new X9ECParameters(curve, G, curve.getOrder(), curve.getCofactor(), S);
+ }
+ };
+
+ static final Hashtable nameToCurve = new Hashtable();
+ static final Hashtable nameToOID = new Hashtable();
+ static final Hashtable oidToCurve = new Hashtable();
+ static final Hashtable oidToName = new Hashtable();
+
+ static void defineCurve(String name, X9ECParametersHolder holder)
+ {
+ nameToCurve.put(name, holder);
+ }
+
+ static void defineCurveWithOID(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder)
+ {
+ nameToCurve.put(name, holder);
+ nameToOID.put(name, oid);
+ oidToName.put(oid, name);
+ oidToCurve.put(oid, holder);
+ }
+
+ static void defineCurveAlias(String alias, ASN1ObjectIdentifier oid)
+ {
+ alias = Strings.toLowerCase(alias);
+ nameToOID.put(alias, oid);
+ nameToCurve.put(alias, oidToCurve.get(oid));
+ }
+
+ static
+ {
+ defineCurve("curve25519", curve25519);
+
+ defineCurveWithOID("secp192k1", SECObjectIdentifiers.secp192k1, secp192k1);
+ defineCurveWithOID("secp192r1", SECObjectIdentifiers.secp192r1, secp192r1);
+ defineCurveWithOID("secp224k1", SECObjectIdentifiers.secp224k1, secp224k1);
+ defineCurveWithOID("secp224r1", SECObjectIdentifiers.secp224r1, secp224r1);
+ defineCurveWithOID("secp256k1", SECObjectIdentifiers.secp256k1, secp256k1);
+ defineCurveWithOID("secp256r1", SECObjectIdentifiers.secp256r1, secp256r1);
+ defineCurveWithOID("secp384r1", SECObjectIdentifiers.secp384r1, secp384r1);
+ defineCurveWithOID("secp521r1", SECObjectIdentifiers.secp521r1, secp521r1);
+
+ defineCurveAlias("P-192", SECObjectIdentifiers.secp192r1);
+ defineCurveAlias("P-224", SECObjectIdentifiers.secp224r1);
+ defineCurveAlias("P-256", SECObjectIdentifiers.secp256r1);
+ defineCurveAlias("P-384", SECObjectIdentifiers.secp384r1);
+ defineCurveAlias("P-521", SECObjectIdentifiers.secp521r1);
+ }
+
+ public static X9ECParameters getByName(String name)
+ {
+ X9ECParametersHolder holder = (X9ECParametersHolder)nameToCurve.get(Strings.toLowerCase(name));
+ return holder == null ? null : holder.getParameters();
+ }
+
+ /**
+ * return the X9ECParameters object for the named curve represented by the passed in object
+ * identifier. Null if the curve isn't present.
+ *
+ * @param oid
+ * an object identifier representing a named curve, if present.
+ */
+ public static X9ECParameters getByOID(ASN1ObjectIdentifier oid)
+ {
+ X9ECParametersHolder holder = (X9ECParametersHolder)oidToCurve.get(oid);
+ return holder == null ? null : holder.getParameters();
+ }
+
+ /**
+ * return the object identifier signified by the passed in name. Null if there is no object
+ * identifier associated with name.
+ *
+ * @return the object identifier associated with name, if present.
+ */
+ public static ASN1ObjectIdentifier getOID(String name)
+ {
+ return (ASN1ObjectIdentifier)nameToOID.get(Strings.toLowerCase(name));
+ }
+
+ /**
+ * return the named curve name represented by the given object identifier.
+ */
+ public static String getName(ASN1ObjectIdentifier oid)
+ {
+ return (String)oidToName.get(oid);
+ }
+
+ /**
+ * returns an enumeration containing the name strings for curves contained in this structure.
+ */
+ public static Enumeration getNames()
+ {
+ return nameToCurve.keys();
+ }
+}
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 19c0beb7..2f1c9374 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()).normalize();
+ return pair.getY().subtract(tmp).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 2a0b78db..48fc0467 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
@@ -4,9 +4,12 @@ import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
/**
* this does your basic ElGamal encryption algorithm using EC
@@ -61,13 +64,23 @@ public class ECElGamalEncryptor
throw new IllegalStateException("ECElGamalEncryptor not initialised");
}
- BigInteger n = key.getParameters().getN();
- BigInteger k = ECUtil.generateK(n, random);
+ ECDomainParameters ec = key.getParameters();
+ BigInteger k = ECUtil.generateK(ec.getN(), random);
- ECPoint g = key.getParameters().getG();
- ECPoint gamma = g.multiply(k);
- ECPoint phi = key.getQ().multiply(k).add(point);
+ ECMultiplier basePointMultiplier = createBasePointMultiplier();
- return new ECPair(gamma.normalize(), phi.normalize());
+ ECPoint[] gamma_phi = new ECPoint[]{
+ basePointMultiplier.multiply(ec.getG(), k),
+ key.getQ().multiply(k).add(point)
+ };
+
+ ec.getCurve().normalizeAll(gamma_phi);
+
+ return new ECPair(gamma_phi[0], gamma_phi[1]);
+ }
+
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
index e35e077c..2c6a920b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
@@ -3,8 +3,11 @@ package org.bouncycastle.crypto.ec;
import java.math.BigInteger;
import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
/**
* this transforms the original randomness used for an ElGamal encryption by a fixed value.
@@ -38,7 +41,7 @@ public class ECFixedTransform
}
/**
- * Transform an existing cipher test pair using the ElGamal algorithm. Note: it is assumed this
+ * Transform an existing cipher text 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.
*
@@ -52,11 +55,20 @@ public class ECFixedTransform
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());
+ ECDomainParameters ec = key.getParameters();
+ BigInteger n = ec.getN();
- return new ECPair(cipherText.getX().add(gamma).normalize(), phi.normalize());
+ ECMultiplier basePointMultiplier = createBasePointMultiplier();
+ BigInteger k = this.k.mod(n);
+
+ ECPoint[] gamma_phi = new ECPoint[]{
+ basePointMultiplier.multiply(ec.getG(), k).add(cipherText.getX()),
+ key.getQ().multiply(k).add(cipherText.getY())
+ };
+
+ ec.getCurve().normalizeAll(gamma_phi);
+
+ return new ECPair(gamma_phi[0], gamma_phi[1]);
}
/**
@@ -68,4 +80,9 @@ public class ECFixedTransform
{
return k;
}
+
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
}
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 74016c18..112d20c1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
@@ -4,9 +4,12 @@ import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
/**
* this does your basic Elgamal encryption algorithm using EC
@@ -49,7 +52,7 @@ public class ECNewPublicKeyTransform
}
/**
- * Transform an existing cipher test pair using the ElGamal algorithm. Note: the input cipherText will
+ * Transform an existing cipher text pair using the ElGamal algorithm. Note: the input cipherText will
* need to be preserved in order to complete the transformation to the new public key.
*
* @param cipherText the EC point to process.
@@ -62,13 +65,24 @@ public class ECNewPublicKeyTransform
throw new IllegalStateException("ECNewPublicKeyTransform not initialised");
}
- BigInteger n = key.getParameters().getN();
- BigInteger k = ECUtil.generateK(n, random);
+ ECDomainParameters ec = key.getParameters();
+ BigInteger n = ec.getN();
- ECPoint g = key.getParameters().getG();
- ECPoint gamma = g.multiply(k);
- ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
+ ECMultiplier basePointMultiplier = createBasePointMultiplier();
+ BigInteger k = ECUtil.generateK(n, random);
- return new ECPair(gamma.normalize(), phi.normalize());
+ ECPoint[] gamma_phi = new ECPoint[]{
+ basePointMultiplier.multiply(ec.getG(), k),
+ key.getQ().multiply(k).add(cipherText.getY())
+ };
+
+ ec.getCurve().normalizeAll(gamma_phi);
+
+ return new ECPair(gamma_phi[0], gamma_phi[1]);
+ }
+
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
}
}
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 b293759a..7bfc0b30 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
@@ -4,9 +4,12 @@ import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
/**
* this transforms the original randomness used for an ElGamal encryption.
@@ -66,16 +69,23 @@ public class ECNewRandomnessTransform
throw new IllegalStateException("ECNewRandomnessTransform not initialised");
}
- BigInteger n = key.getParameters().getN();
- BigInteger k = ECUtil.generateK(n, random);
- ECPoint g = key.getParameters().getG();
- ECPoint gamma = g.multiply(k);
- ECPoint phi = key.getQ().multiply(k).add(cipherText.getY());
+ ECDomainParameters ec = key.getParameters();
+ BigInteger n = ec.getN();
+
+ ECMultiplier basePointMultiplier = createBasePointMultiplier();
+ BigInteger k = ECUtil.generateK(n, random);
+
+ ECPoint[] gamma_phi = new ECPoint[]{
+ basePointMultiplier.multiply(ec.getG(), k).add(cipherText.getX()),
+ key.getQ().multiply(k).add(cipherText.getY())
+ };
+
+ ec.getCurve().normalizeAll(gamma_phi);
lastK = k;
- return new ECPair(cipherText.getX().add(gamma).normalize(), phi.normalize());
+ return new ECPair(gamma_phi[0], gamma_phi[1]);
}
/**
@@ -87,4 +97,9 @@ public class ECNewRandomnessTransform
{
return lastK;
}
+
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java
index d21d8fd3..74921f09 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECUtil.java
@@ -9,14 +9,13 @@ class ECUtil
{
static BigInteger generateK(BigInteger n, SecureRandom random)
{
- int nBitLength = n.bitLength();
- BigInteger k = new BigInteger(nBitLength, random);
-
- while (k.equals(ECConstants.ZERO) || (k.compareTo(n) >= 0))
+ int nBitLength = n.bitLength();
+ BigInteger k;
+ do
{
k = new BigInteger(nBitLength, random);
}
-
+ while (k.equals(ECConstants.ZERO) || (k.compareTo(n) >= 0));
return k;
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html
new file mode 100644
index 00000000..223823e7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Utility classes for support Elliptic Curve cryptographic transforms.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/AllTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/AllTests.java
new file mode 100644
index 00000000..ba75c06b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/AllTests.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.crypto.ec.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.bouncycastle.util.test.SimpleTestResult;
+
+public class AllTests
+ extends TestCase
+{
+ public void testCrypto()
+ {
+ org.bouncycastle.util.test.Test[] tests = { new ECElGamalTest(), new ECTransformationTest() };
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ SimpleTestResult result = (SimpleTestResult)tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ fail(result.toString());
+ }
+ }
+ }
+
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("Lightweight EC ElGamal Tests");
+
+ suite.addTestSuite(AllTests.class);
+
+ return suite;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/ECElGamalTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/ECElGamalTest.java
new file mode 100644
index 00000000..629c1a43
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/ECElGamalTest.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.crypto.ec.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.ec.ECDecryptor;
+import org.bouncycastle.crypto.ec.ECElGamalDecryptor;
+import org.bouncycastle.crypto.ec.ECElGamalEncryptor;
+import org.bouncycastle.crypto.ec.ECEncryptor;
+import org.bouncycastle.crypto.ec.ECPair;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class ECElGamalTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "ECElGamal";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BigInteger n = new BigInteger("6277101735386680763835789423176059013767194773182842284081");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ n);
+
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+ ParametersWithRandom pRandom = new ParametersWithRandom(pubKey, new SecureRandom());
+
+ doTest(priKey, pRandom, BigInteger.valueOf(20));
+
+ BigInteger rand = new BigInteger(pubKey.getParameters().getN().bitLength() - 1, new SecureRandom());
+
+ doTest(priKey, pRandom, rand);
+ }
+
+ private void doTest(ECPrivateKeyParameters priKey, ParametersWithRandom pRandom, BigInteger value)
+ {
+ ECPoint data = priKey.getParameters().getG().multiply(value);
+
+ ECEncryptor encryptor = new ECElGamalEncryptor();
+
+ encryptor.init(pRandom);
+
+ ECPair pair = encryptor.encrypt(data);
+
+ ECDecryptor decryptor = new ECElGamalDecryptor();
+
+ decryptor.init(priKey);
+
+ ECPoint result = decryptor.decrypt(pair);
+
+ if (!data.equals(result))
+ {
+ fail("point pair failed to decrypt back to original");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new ECElGamalTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/ECTransformationTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/ECTransformationTest.java
new file mode 100644
index 00000000..96ada144
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/test/ECTransformationTest.java
@@ -0,0 +1,149 @@
+package org.bouncycastle.crypto.ec.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.ec.ECDecryptor;
+import org.bouncycastle.crypto.ec.ECElGamalDecryptor;
+import org.bouncycastle.crypto.ec.ECElGamalEncryptor;
+import org.bouncycastle.crypto.ec.ECEncryptor;
+import org.bouncycastle.crypto.ec.ECNewPublicKeyTransform;
+import org.bouncycastle.crypto.ec.ECNewRandomnessTransform;
+import org.bouncycastle.crypto.ec.ECPair;
+import org.bouncycastle.crypto.ec.ECPairTransform;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class ECTransformationTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "ECTransformationTest";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BigInteger n = new BigInteger("6277101735386680763835789423176059013767194773182842284081");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ n);
+
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+
+ ParametersWithRandom pRandom = new ParametersWithRandom(pubKey, new SecureRandom());
+
+ doTest(priKey, pRandom, BigInteger.valueOf(20));
+
+ BigInteger rand = new BigInteger(pubKey.getParameters().getN().bitLength() - 1, new SecureRandom());
+
+ doTest(priKey, pRandom, rand);
+ doSameKeyTest(priKey, pRandom, rand);
+ }
+
+ private void doTest(ECPrivateKeyParameters priKey, ParametersWithRandom pRandom, BigInteger value)
+ {
+ ECPoint data = priKey.getParameters().getG().multiply(value);
+
+ ECEncryptor encryptor = new ECElGamalEncryptor();
+
+ encryptor.init(pRandom);
+
+ ECPair pair = encryptor.encrypt(data);
+
+ ECKeyPairGenerator ecGen = new ECKeyPairGenerator();
+
+ ecGen.init(new ECKeyGenerationParameters(priKey.getParameters(), new SecureRandom()));
+
+ AsymmetricCipherKeyPair reEncKP = ecGen.generateKeyPair();
+
+ ECPairTransform ecr = new ECNewPublicKeyTransform();
+
+ ecr.init(reEncKP.getPublic());
+
+ ECPair srcPair = pair;
+
+ // re-encrypt the message portion
+ pair = ecr.transform(srcPair);
+
+ ECDecryptor decryptor = new ECElGamalDecryptor();
+
+ decryptor.init(priKey);
+
+ // decrypt out the original private key
+ ECPoint p = decryptor.decrypt(new ECPair(srcPair.getX(), pair.getY()));
+
+ decryptor.init(reEncKP.getPrivate());
+
+ // decrypt the fully transformed point.
+ ECPoint result = decryptor.decrypt(new ECPair(pair.getX(), p));
+
+ if (!data.equals(result))
+ {
+ fail("point pair failed to decrypt back to original");
+ }
+ }
+
+ private void doSameKeyTest(ECPrivateKeyParameters priKey, ParametersWithRandom pRandom, BigInteger value)
+ {
+ ECPoint data = priKey.getParameters().getG().multiply(value);
+
+ ECEncryptor encryptor = new ECElGamalEncryptor();
+
+ encryptor.init(pRandom);
+
+ ECPair pair = encryptor.encrypt(data);
+
+ ECPairTransform ecr = new ECNewRandomnessTransform();
+
+ ecr.init(pRandom);
+
+ ECPair srcPair = pair;
+
+ // re-encrypt the message portion
+ pair = ecr.transform(srcPair);
+
+ ECDecryptor decryptor = new ECElGamalDecryptor();
+
+ decryptor.init(priKey);
+
+ // decrypt the fully transformed point.
+ ECPoint result = decryptor.decrypt(pair);
+
+ if (!data.equals(result))
+ {
+ fail("point pair failed to decrypt back to original");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new ECTransformationTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
index 09f1537e..f9acbd42 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/PKCS1Encoding.java
@@ -34,6 +34,8 @@ public class PKCS1Encoding
private boolean forEncryption;
private boolean forPrivateKey;
private boolean useStrictLength;
+ private int pLen = -1;
+ private byte[] fallback = null;
/**
* Basic constructor.
@@ -46,6 +48,42 @@ public class PKCS1Encoding
this.useStrictLength = useStrict();
}
+ /**
+ * Constructor for decryption with a fixed plaintext length.
+ *
+ * @param cipher The cipher to use for cryptographic operation.
+ * @param pLen Length of the expected plaintext.
+ */
+ public PKCS1Encoding(
+ AsymmetricBlockCipher cipher,
+ int pLen)
+ {
+ this.engine = cipher;
+ this.useStrictLength = useStrict();
+ this.pLen = pLen;
+ }
+
+ /**
+ * Constructor for decryption with a fixed plaintext length and a fallback
+ * value that is returned, if the padding is incorrect.
+ *
+ * @param cipher
+ * The cipher to use for cryptographic operation.
+ * @param fallback
+ * The fallback value, we don't do an arraycopy here.
+ */
+ public PKCS1Encoding(
+ AsymmetricBlockCipher cipher,
+ byte[] fallback)
+ {
+ this.engine = cipher;
+ this.useStrictLength = useStrict();
+ this.fallback = fallback;
+ this.pLen = fallback.length;
+ }
+
+
+
//
// for J2ME compatibility
//
@@ -183,6 +221,121 @@ public class PKCS1Encoding
return engine.processBlock(block, 0, block.length);
}
+
+ /**
+ * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext
+ * for encryption.
+ *
+ * @param encoded The Plaintext.
+ * @param pLen Expected length of the plaintext.
+ * @return Either 0, if the encoding is correct, or -1, if it is incorrect.
+ */
+ private static int checkPkcs1Encoding(byte[] encoded, int pLen) {
+ int correct = 0;
+ /*
+ * Check if the first two bytes are 0 2
+ */
+ correct |= (encoded[0] ^ 2);
+
+ /*
+ * Now the padding check, check for no 0 byte in the padding
+ */
+ int plen = encoded.length - (
+ pLen /* Lenght of the PMS */
+ + 1 /* Final 0-byte before PMS */
+ );
+
+ for (int i = 1; i < plen; i++) {
+ int tmp = encoded[i];
+ tmp |= tmp >> 1;
+ tmp |= tmp >> 2;
+ tmp |= tmp >> 4;
+ correct |= (tmp & 1) - 1;
+ }
+
+ /*
+ * Make sure the padding ends with a 0 byte.
+ */
+ correct |= encoded[encoded.length - (pLen +1)];
+
+ /*
+ * Return 0 or 1, depending on the result.
+ */
+ correct |= correct >> 1;
+ correct |= correct >> 2;
+ correct |= correct >> 4;
+ return ~((correct & 1) - 1);
+ }
+
+
+ /**
+ * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct.
+ *
+ * @param in The encrypted block.
+ * @param inOff Offset in the encrypted block.
+ * @param inLen Length of the encrypted block.
+ * //@param pLen Length of the desired output.
+ * @return The plaintext without padding, or a random value if the padding was incorrect.
+ *
+ * @throws InvalidCipherTextException
+ */
+ private byte[] decodeBlockOrRandom(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+ if (!forPrivateKey)
+ {
+ throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
+ }
+
+ byte[] block = engine.processBlock(in, inOff, inLen);
+ byte[] random = null;
+ if (this.fallback == null)
+ {
+ random = new byte[this.pLen];
+ this.random.nextBytes(random);
+ }
+ else
+ {
+ random = fallback;
+ }
+
+ /*
+ * TODO: This is a potential dangerous side channel. However, you can
+ * fix this by changing the RSA engine in a way, that it will always
+ * return blocks of the same length and prepend them with 0 bytes if
+ * needed.
+ */
+ if (block.length < getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block truncated");
+ }
+
+ /*
+ * TODO: Potential side channel. Fix it by making the engine always
+ * return blocks of the correct length.
+ */
+ if (useStrictLength && block.length != engine.getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block incorrect size");
+ }
+
+ /*
+ * Check the padding.
+ */
+ int correct = PKCS1Encoding.checkPkcs1Encoding(block, this.pLen);
+
+ /*
+ * Now, to a constant time constant memory copy of the decrypted value
+ * or the random value, depending on the validity of the padding.
+ */
+ byte[] result = new byte[this.pLen];
+ for (int i = 0; i < this.pLen; i++)
+ {
+ result[i] = (byte)((block[i + (block.length - pLen)] & (~correct)) | (random[i] & correct));
+ }
+
+ return result;
+ }
/**
* @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
@@ -193,7 +346,16 @@ public class PKCS1Encoding
int inLen)
throws InvalidCipherTextException
{
- byte[] block = engine.processBlock(in, inOff, inLen);
+ /*
+ * If the length of the expected plaintext is known, we use a constant-time decryption.
+ * If the decryption fails, we return a random value.
+ */
+ if (this.pLen != -1)
+ {
+ return this.decodeBlockOrRandom(in, inOff, inLen);
+ }
+
+ byte[] block = engine.processBlock(in, inOff, inLen);
if (block.length < getOutputBlockSize())
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html
new file mode 100644
index 00000000..fc56f634
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Block encodings for asymmetric ciphers.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
index 756197ce..a0fd0840 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESEngine.java
@@ -473,74 +473,65 @@ private static final int[] Tinv0 =
private void encryptBlock(int[][] KW)
{
- int r, r0, r1, r2, r3;
-
- C0 ^= KW[0][0];
- C1 ^= KW[0][1];
- C2 ^= KW[0][2];
- C3 ^= KW[0][3];
-
- r = 1;
+ int t0 = this.C0 ^ KW[0][0];
+ int t1 = this.C1 ^ KW[0][1];
+ int t2 = this.C2 ^ KW[0][2];
+ int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
while (r < ROUNDS - 1)
{
- r0 = T0[C0&255] ^ shift(T0[(C1>>8)&255], 24) ^ shift(T0[(C2>>16)&255],16) ^ shift(T0[(C3>>24)&255],8) ^ KW[r][0];
- r1 = T0[C1&255] ^ shift(T0[(C2>>8)&255], 24) ^ shift(T0[(C3>>16)&255], 16) ^ shift(T0[(C0>>24)&255], 8) ^ KW[r][1];
- r2 = T0[C2&255] ^ shift(T0[(C3>>8)&255], 24) ^ shift(T0[(C0>>16)&255], 16) ^ shift(T0[(C1>>24)&255], 8) ^ KW[r][2];
- r3 = T0[C3&255] ^ shift(T0[(C0>>8)&255], 24) ^ shift(T0[(C1>>16)&255], 16) ^ shift(T0[(C2>>24)&255], 8) ^ KW[r++][3];
- C0 = T0[r0&255] ^ shift(T0[(r1>>8)&255], 24) ^ shift(T0[(r2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
- C1 = T0[r1&255] ^ shift(T0[(r2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(r0>>24)&255], 8) ^ KW[r][1];
- C2 = T0[r2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(r0>>16)&255], 16) ^ shift(T0[(r1>>24)&255], 8) ^ KW[r][2];
- C3 = T0[r3&255] ^ shift(T0[(r0>>8)&255], 24) ^ shift(T0[(r1>>16)&255], 16) ^ shift(T0[(r2>>24)&255], 8) ^ KW[r++][3];
+ r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
+ r1 = T0[t1&255] ^ shift(T0[(t2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(t0>>24)&255], 8) ^ KW[r][1];
+ r2 = T0[t2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(t0>>16)&255], 16) ^ shift(T0[(t1>>24)&255], 8) ^ KW[r][2];
+ r3 = T0[r3&255] ^ shift(T0[(t0>>8)&255], 24) ^ shift(T0[(t1>>16)&255], 16) ^ shift(T0[(t2>>24)&255], 8) ^ KW[r++][3];
+ t0 = T0[r0&255] ^ shift(T0[(r1>>8)&255], 24) ^ shift(T0[(r2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
+ t1 = T0[r1&255] ^ shift(T0[(r2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(r0>>24)&255], 8) ^ KW[r][1];
+ t2 = T0[r2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(r0>>16)&255], 16) ^ shift(T0[(r1>>24)&255], 8) ^ KW[r][2];
+ r3 = T0[r3&255] ^ shift(T0[(r0>>8)&255], 24) ^ shift(T0[(r1>>16)&255], 16) ^ shift(T0[(r2>>24)&255], 8) ^ KW[r++][3];
}
- r0 = T0[C0&255] ^ shift(T0[(C1>>8)&255], 24) ^ shift(T0[(C2>>16)&255], 16) ^ shift(T0[(C3>>24)&255], 8) ^ KW[r][0];
- r1 = T0[C1&255] ^ shift(T0[(C2>>8)&255], 24) ^ shift(T0[(C3>>16)&255], 16) ^ shift(T0[(C0>>24)&255], 8) ^ KW[r][1];
- r2 = T0[C2&255] ^ shift(T0[(C3>>8)&255], 24) ^ shift(T0[(C0>>16)&255], 16) ^ shift(T0[(C1>>24)&255], 8) ^ KW[r][2];
- r3 = T0[C3&255] ^ shift(T0[(C0>>8)&255], 24) ^ shift(T0[(C1>>16)&255], 16) ^ shift(T0[(C2>>24)&255], 8) ^ KW[r++][3];
+ r0 = T0[t0&255] ^ shift(T0[(t1>>8)&255], 24) ^ shift(T0[(t2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
+ r1 = T0[t1&255] ^ shift(T0[(t2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(t0>>24)&255], 8) ^ KW[r][1];
+ r2 = T0[t2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(t0>>16)&255], 16) ^ shift(T0[(t1>>24)&255], 8) ^ KW[r][2];
+ r3 = T0[r3&255] ^ shift(T0[(t0>>8)&255], 24) ^ shift(T0[(t1>>16)&255], 16) ^ shift(T0[(t2>>24)&255], 8) ^ KW[r++][3];
// the final round's table is a simple function of S so we don't use a whole other four tables for it
- C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
- C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
- C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
- C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
-
+ this.C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
+ this.C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
+ this.C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+ this.C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
}
private void decryptBlock(int[][] KW)
{
- int r, r0, r1, r2, r3;
-
- C0 ^= KW[ROUNDS][0];
- C1 ^= KW[ROUNDS][1];
- C2 ^= KW[ROUNDS][2];
- C3 ^= KW[ROUNDS][3];
-
- r = ROUNDS-1;
+ int t0 = this.C0 ^ KW[ROUNDS][0];
+ int t1 = this.C1 ^ KW[ROUNDS][1];
+ int t2 = this.C2 ^ KW[ROUNDS][2];
- while (r>1)
+ int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+ while (r > 1)
{
- r0 = Tinv0[C0&255] ^ shift(Tinv0[(C3>>8)&255], 24) ^ shift(Tinv0[(C2>>16)&255], 16) ^ shift(Tinv0[(C1>>24)&255], 8) ^ KW[r][0];
- r1 = Tinv0[C1&255] ^ shift(Tinv0[(C0>>8)&255], 24) ^ shift(Tinv0[(C3>>16)&255], 16) ^ shift(Tinv0[(C2>>24)&255], 8) ^ KW[r][1];
- r2 = Tinv0[C2&255] ^ shift(Tinv0[(C1>>8)&255], 24) ^ shift(Tinv0[(C0>>16)&255], 16) ^ shift(Tinv0[(C3>>24)&255], 8) ^ KW[r][2];
- r3 = Tinv0[C3&255] ^ shift(Tinv0[(C2>>8)&255], 24) ^ shift(Tinv0[(C1>>16)&255], 16) ^ shift(Tinv0[(C0>>24)&255], 8) ^ KW[r--][3];
- C0 = Tinv0[r0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(r2>>16)&255], 16) ^ shift(Tinv0[(r1>>24)&255], 8) ^ KW[r][0];
- C1 = Tinv0[r1&255] ^ shift(Tinv0[(r0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(r2>>24)&255], 8) ^ KW[r][1];
- C2 = Tinv0[r2&255] ^ shift(Tinv0[(r1>>8)&255], 24) ^ shift(Tinv0[(r0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
- C3 = Tinv0[r3&255] ^ shift(Tinv0[(r2>>8)&255], 24) ^ shift(Tinv0[(r1>>16)&255], 16) ^ shift(Tinv0[(r0>>24)&255], 8) ^ KW[r--][3];
+ r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
+ r1 = Tinv0[t1&255] ^ shift(Tinv0[(t0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(t2>>24)&255], 8) ^ KW[r][1];
+ r2 = Tinv0[t2&255] ^ shift(Tinv0[(t1>>8)&255], 24) ^ shift(Tinv0[(t0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
+ r3 = Tinv0[r3&255] ^ shift(Tinv0[(t2>>8)&255], 24) ^ shift(Tinv0[(t1>>16)&255], 16) ^ shift(Tinv0[(t0>>24)&255], 8) ^ KW[r--][3];
+ t0 = Tinv0[r0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(r2>>16)&255], 16) ^ shift(Tinv0[(r1>>24)&255], 8) ^ KW[r][0];
+ t1 = Tinv0[r1&255] ^ shift(Tinv0[(r0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(r2>>24)&255], 8) ^ KW[r][1];
+ t2 = Tinv0[r2&255] ^ shift(Tinv0[(r1>>8)&255], 24) ^ shift(Tinv0[(r0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
+ r3 = Tinv0[r3&255] ^ shift(Tinv0[(r2>>8)&255], 24) ^ shift(Tinv0[(r1>>16)&255], 16) ^ shift(Tinv0[(r0>>24)&255], 8) ^ KW[r--][3];
}
- r0 = Tinv0[C0&255] ^ shift(Tinv0[(C3>>8)&255], 24) ^ shift(Tinv0[(C2>>16)&255], 16) ^ shift(Tinv0[(C1>>24)&255], 8) ^ KW[r][0];
- r1 = Tinv0[C1&255] ^ shift(Tinv0[(C0>>8)&255], 24) ^ shift(Tinv0[(C3>>16)&255], 16) ^ shift(Tinv0[(C2>>24)&255], 8) ^ KW[r][1];
- r2 = Tinv0[C2&255] ^ shift(Tinv0[(C1>>8)&255], 24) ^ shift(Tinv0[(C0>>16)&255], 16) ^ shift(Tinv0[(C3>>24)&255], 8) ^ KW[r][2];
- r3 = Tinv0[C3&255] ^ shift(Tinv0[(C2>>8)&255], 24) ^ shift(Tinv0[(C1>>16)&255], 16) ^ shift(Tinv0[(C0>>24)&255], 8) ^ KW[r][3];
+ r0 = Tinv0[t0&255] ^ shift(Tinv0[(r3>>8)&255], 24) ^ shift(Tinv0[(t2>>16)&255], 16) ^ shift(Tinv0[(t1>>24)&255], 8) ^ KW[r][0];
+ r1 = Tinv0[t1&255] ^ shift(Tinv0[(t0>>8)&255], 24) ^ shift(Tinv0[(r3>>16)&255], 16) ^ shift(Tinv0[(t2>>24)&255], 8) ^ KW[r][1];
+ r2 = Tinv0[t2&255] ^ shift(Tinv0[(t1>>8)&255], 24) ^ shift(Tinv0[(t0>>16)&255], 16) ^ shift(Tinv0[(r3>>24)&255], 8) ^ KW[r][2];
+ r3 = Tinv0[r3&255] ^ shift(Tinv0[(t2>>8)&255], 24) ^ shift(Tinv0[(t1>>16)&255], 16) ^ shift(Tinv0[(t0>>24)&255], 8) ^ KW[r][3];
// the final round's table is a simple function of Si so we don't use a whole other four tables for it
- C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
- C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
- C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
- C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
+ this.C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+ this.C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
+ this.C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
+ this.C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
index ff4b2f8f..e2b00d3a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESFastEngine.java
@@ -5,6 +5,7 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Pack;
/**
* an implementation of the AES (Rijndael), from FIPS-197.
@@ -110,8 +111,9 @@ public class AESFastEngine
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
// precomputation tables of calculations for rounds
- private static final int[] T0 =
+ private static final int[] T =
{
+ // T0
0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
@@ -163,10 +165,9 @@ public class AESFastEngine
0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
- 0x3a16162c};
+ 0x3a16162c,
- private static final int[] T1 =
- {
+ // T1
0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d,
0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203,
0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
@@ -218,10 +219,9 @@ public class AESFastEngine
0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da,
0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0,
0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6,
- 0x16162c3a};
+ 0x16162c3a,
- private static final int[] T2 =
- {
+ // T2
0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2,
0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301,
0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
@@ -273,10 +273,9 @@ public class AESFastEngine
0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf,
0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099,
0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb,
- 0x162c3a16};
+ 0x162c3a16,
- private static final int[] T3 =
- {
+ // T3
0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2,
0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101,
0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
@@ -330,8 +329,9 @@ public class AESFastEngine
0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb,
0x2c3a1616};
- private static final int[] Tinv0 =
+ private static final int[] Tinv =
{
+ // Tinv0
0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
@@ -383,10 +383,9 @@ public class AESFastEngine
0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
- 0x4257b8d0};
+ 0x4257b8d0,
- private static final int[] Tinv1 =
- {
+ // Tinv1
0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb,
0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6,
0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
@@ -438,10 +437,9 @@ public class AESFastEngine
0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672,
0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de,
0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874,
- 0x57b8d042};
+ 0x57b8d042,
- private static final int[] Tinv2 =
- {
+ // Tinv2
0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b,
0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d,
0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
@@ -493,10 +491,9 @@ public class AESFastEngine
0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3,
0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3,
0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c,
- 0xb8d04257};
+ 0xb8d04257,
- private static final int[] Tinv3 =
- {
+ // Tinv3
0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab,
0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76,
0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
@@ -586,10 +583,11 @@ public class AESFastEngine
return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
}
-
private static int subWord(int x)
{
- return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
+ int i0 = x, i1 = x >>> 8, i2 = x >>> 16, i3 = x >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ return i0 | i1 << 8 | i2 << 16 | i3 << 24;
}
/**
@@ -727,19 +725,19 @@ public class AESFastEngine
throw new OutputLengthException("output buffer too short");
}
+ unpackBlock(in, inOff);
+
if (forEncryption)
{
- unpackBlock(in, inOff);
encryptBlock(WorkingKey);
- packBlock(out, outOff);
}
else
{
- unpackBlock(in, inOff);
decryptBlock(WorkingKey);
- packBlock(out, outOff);
}
+ packBlock(out, outOff);
+
return BLOCK_SIZE;
}
@@ -747,129 +745,184 @@ public class AESFastEngine
{
}
- private void unpackBlock(
- byte[] bytes,
- int off)
+ private void unpackBlock(byte[] bytes, int off)
{
- int index = off;
-
- C0 = (bytes[index++] & 0xff);
- C0 |= (bytes[index++] & 0xff) << 8;
- C0 |= (bytes[index++] & 0xff) << 16;
- C0 |= bytes[index++] << 24;
-
- C1 = (bytes[index++] & 0xff);
- C1 |= (bytes[index++] & 0xff) << 8;
- C1 |= (bytes[index++] & 0xff) << 16;
- C1 |= bytes[index++] << 24;
-
- C2 = (bytes[index++] & 0xff);
- C2 |= (bytes[index++] & 0xff) << 8;
- C2 |= (bytes[index++] & 0xff) << 16;
- C2 |= bytes[index++] << 24;
-
- C3 = (bytes[index++] & 0xff);
- C3 |= (bytes[index++] & 0xff) << 8;
- C3 |= (bytes[index++] & 0xff) << 16;
- C3 |= bytes[index++] << 24;
+ this.C0 = Pack.littleEndianToInt(bytes, off);
+ this.C1 = Pack.littleEndianToInt(bytes, off + 4);
+ this.C2 = Pack.littleEndianToInt(bytes, off + 8);
+ this.C3 = Pack.littleEndianToInt(bytes, off + 12);
}
- private void packBlock(
- byte[] bytes,
- int off)
+ private void packBlock(byte[] bytes, int off)
{
- int index = off;
-
- bytes[index++] = (byte)C0;
- bytes[index++] = (byte)(C0 >> 8);
- bytes[index++] = (byte)(C0 >> 16);
- bytes[index++] = (byte)(C0 >> 24);
-
- bytes[index++] = (byte)C1;
- bytes[index++] = (byte)(C1 >> 8);
- bytes[index++] = (byte)(C1 >> 16);
- bytes[index++] = (byte)(C1 >> 24);
-
- bytes[index++] = (byte)C2;
- bytes[index++] = (byte)(C2 >> 8);
- bytes[index++] = (byte)(C2 >> 16);
- bytes[index++] = (byte)(C2 >> 24);
-
- bytes[index++] = (byte)C3;
- bytes[index++] = (byte)(C3 >> 8);
- bytes[index++] = (byte)(C3 >> 16);
- bytes[index++] = (byte)(C3 >> 24);
+ Pack.intToLittleEndian(this.C0, bytes, off);
+ Pack.intToLittleEndian(this.C1, bytes, off + 4);
+ Pack.intToLittleEndian(this.C2, bytes, off + 8);
+ Pack.intToLittleEndian(this.C3, bytes, off + 12);
}
private void encryptBlock(int[][] KW)
{
- int r, r0, r1, r2, r3;
-
- C0 ^= KW[0][0];
- C1 ^= KW[0][1];
- C2 ^= KW[0][2];
- C3 ^= KW[0][3];
+ int t0 = this.C0 ^ KW[0][0];
+ int t1 = this.C1 ^ KW[0][1];
+ int t2 = this.C2 ^ KW[0][2];
+
+ /*
+ * Fast engine has precomputed rotr(T0, 8/16/24) tables T1/T2/T3.
+ *
+ * Placing all precomputes in one array requires offsets additions for 8/16/24 rotations but
+ * avoids additional array range checks on 3 more arrays (which on HotSpot are more
+ * expensive than the offset additions).
+ */
+ int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+ int i0, i1, i2, i3;
- r = 1;
while (r < ROUNDS - 1)
{
- r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[(C3>>24)&255] ^ KW[r][0];
- r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[(C0>>24)&255] ^ KW[r][1];
- r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[(C1>>24)&255] ^ KW[r][2];
- r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[(C2>>24)&255] ^ KW[r++][3];
- C0 = T0[r0&255] ^ T1[(r1>>8)&255] ^ T2[(r2>>16)&255] ^ T3[(r3>>24)&255] ^ KW[r][0];
- C1 = T0[r1&255] ^ T1[(r2>>8)&255] ^ T2[(r3>>16)&255] ^ T3[(r0>>24)&255] ^ KW[r][1];
- C2 = T0[r2&255] ^ T1[(r3>>8)&255] ^ T2[(r0>>16)&255] ^ T3[(r1>>24)&255] ^ KW[r][2];
- C3 = T0[r3&255] ^ T1[(r0>>8)&255] ^ T2[(r1>>16)&255] ^ T3[(r2>>24)&255] ^ KW[r++][3];
+ i0 = t0; i1 = t1 >>> 8; i2 = t2 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r0 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][0];
+
+ i0 = t1; i1 = t2 >>> 8; i2 = r3 >>> 16; i3 = t0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r1 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][1];
+
+ i0 = t2; i1 = r3 >>> 8; i2 = t0 >>> 16; i3 = t1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r2 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = t0 >>> 8; i2 = t1 >>> 16; i3 = t2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r++][3];
+
+ i0 = r0; i1 = r1 >>> 8; i2 = r2 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t0 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][0];
+
+ i0 = r1; i1 = r2 >>> 8; i2 = r3 >>> 16; i3 = r0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t1 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][1];
+
+ i0 = r2; i1 = r3 >>> 8; i2 = r0 >>> 16; i3 = r1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t2 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = r0 >>> 8; i2 = r1 >>> 16; i3 = r2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r++][3];
}
- r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[(C3>>24)&255] ^ KW[r][0];
- r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[(C0>>24)&255] ^ KW[r][1];
- r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[(C1>>24)&255] ^ KW[r][2];
- r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[(C2>>24)&255] ^ KW[r++][3];
-
+ i0 = t0; i1 = t1 >>> 8; i2 = t2 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r0 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][0];
+
+ i0 = t1; i1 = t2 >>> 8; i2 = r3 >>> 16; i3 = t0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r1 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][1];
+
+ i0 = t2; i1 = r3 >>> 8; i2 = t0 >>> 16; i3 = t1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r2 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = t0 >>> 8; i2 = t1 >>> 16; i3 = t2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = T[i0] ^ T[256 + i1] ^ T[512 + i2] ^ T[768 + i3] ^ KW[r++][3];
+
// the final round's table is a simple function of S so we don't use a whole other four tables for it
- C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
- C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
- C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
- C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
+ i0 = r0; i1 = r1 >>> 8; i2 = r2 >>> 16; i3 = r3 >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][0];
+
+ i0 = r1; i1 = r2 >>> 8; i2 = r3 >>> 16; i3 = r0 >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][1];
+ i0 = r2; i1 = r3 >>> 8; i2 = r0 >>> 16; i3 = r1 >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][2];
+
+ i0 = r3; i1 = r0 >>> 8; i2 = r1 >>> 16; i3 = r2 >>> 24;
+ i0 = S[i0 & 255] & 255; i1 = S[i1 & 255] & 255; i2 = S[i2 & 255] & 255; i3 = S[i3 & 255] & 255;
+ this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[r][3];
}
private void decryptBlock(int[][] KW)
{
- int r0, r1, r2, r3;
+ int t0 = this.C0 ^ KW[ROUNDS][0];
+ int t1 = this.C1 ^ KW[ROUNDS][1];
+ int t2 = this.C2 ^ KW[ROUNDS][2];
- C0 ^= KW[ROUNDS][0];
- C1 ^= KW[ROUNDS][1];
- C2 ^= KW[ROUNDS][2];
- C3 ^= KW[ROUNDS][3];
+ int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+ int i0, i1, i2, i3;
- int r = ROUNDS-1;
-
- while (r>1)
+ while (r > 1)
{
- r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[(C1>>24)&255] ^ KW[r][0];
- r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r][1];
- r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r][2];
- r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r--][3];
- C0 = Tinv0[r0&255] ^ Tinv1[(r3>>8)&255] ^ Tinv2[(r2>>16)&255] ^ Tinv3[(r1>>24)&255] ^ KW[r][0];
- C1 = Tinv0[r1&255] ^ Tinv1[(r0>>8)&255] ^ Tinv2[(r3>>16)&255] ^ Tinv3[(r2>>24)&255] ^ KW[r][1];
- C2 = Tinv0[r2&255] ^ Tinv1[(r1>>8)&255] ^ Tinv2[(r0>>16)&255] ^ Tinv3[(r3>>24)&255] ^ KW[r][2];
- C3 = Tinv0[r3&255] ^ Tinv1[(r2>>8)&255] ^ Tinv2[(r1>>16)&255] ^ Tinv3[(r0>>24)&255] ^ KW[r--][3];
+ i0 = t0; i1 = r3 >>> 8; i2 = t2 >>> 16; i3 = t1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r0 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][0];
+
+ i0 = t1; i1 = t0 >>> 8; i2 = r3 >>> 16; i3 = t2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r1 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][1];
+
+ i0 = t2; i1 = t1 >>> 8; i2 = t0 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r2 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = t2 >>> 8; i2 = t1 >>> 16; i3 = t0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r--][3];
+
+ i0 = r0; i1 = r3 >>> 8; i2 = r2 >>> 16; i3 = r1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t0 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][0];
+
+ i0 = r1; i1 = r0 >>> 8; i2 = r3 >>> 16; i3 = r2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t1 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][1];
+
+ i0 = r2; i1 = r1 >>> 8; i2 = r0 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ t2 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r][2];
+
+ i0 = r3; i1 = r2 >>> 8; i2 = r1 >>> 16; i3 = r0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[r--][3];
}
- r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[(C1>>24)&255] ^ KW[r][0];
- r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[(C2>>24)&255] ^ KW[r][1];
- r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[(C3>>24)&255] ^ KW[r][2];
- r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[(C0>>24)&255] ^ KW[r][3];
-
+ i0 = t0; i1 = r3 >>> 8; i2 = t2 >>> 16; i3 = t1 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r0 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[1][0];
+
+ i0 = t1; i1 = t0 >>> 8; i2 = r3 >>> 16; i3 = t2 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r1 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[1][1];
+
+ i0 = t2; i1 = t1 >>> 8; i2 = t0 >>> 16; i3 = r3 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r2 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[1][2];
+
+ i0 = r3; i1 = t2 >>> 8; i2 = t1 >>> 16; i3 = t0 >>> 24;
+ i0 &= 255; i1 &= 255; i2 &= 255; i3 &= 255;
+ r3 = Tinv[i0] ^ Tinv[256 + i1] ^ Tinv[512 + i2] ^ Tinv[768 + i3] ^ KW[1][3];
+
// the final round's table is a simple function of Si so we don't use a whole other four tables for it
- C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
- C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
- C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
- C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
+ i0 = r0; i1 = r3 >>> 8; i2 = r2 >>> 16; i3 = r1 >>> 24;
+ i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
+ this.C0 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][0];
+
+ i0 = r1; i1 = r0 >>> 8; i2 = r3 >>> 16; i3 = r2 >>> 24;
+ i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
+ this.C1 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][1];
+
+ i0 = r2; i1 = r1 >>> 8; i2 = r0 >>> 16; i3 = r3 >>> 24;
+ i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
+ this.C2 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][2];
+
+ i0 = r3; i1 = r2 >>> 8; i2 = r1 >>> 16; i3 = r0 >>> 24;
+ i0 = Si[i0 & 255] & 255; i1 = Si[i1 & 255] & 255; i2 = Si[i2 & 255] & 255; i3 = Si[i3 & 255] & 255;
+ this.C3 = i0 ^ i1 << 8 ^ i2 << 16 ^ i3 << 24 ^ KW[0][3];
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java
index df8444b9..70a3f683 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESLightEngine.java
@@ -370,70 +370,65 @@ public class AESLightEngine
private void encryptBlock(int[][] KW)
{
- int r, r0, r1, r2, r3;
+ int t0 = this.C0 ^ KW[0][0];
+ int t1 = this.C1 ^ KW[0][1];
+ int t2 = this.C2 ^ KW[0][2];
- C0 ^= KW[0][0];
- C1 ^= KW[0][1];
- C2 ^= KW[0][2];
- C3 ^= KW[0][3];
-
- for (r = 1; r < ROUNDS - 1;)
+ int r = 1, r0, r1, r2, r3 = this.C3 ^ KW[0][3];
+ while (r < ROUNDS - 1)
{
- r0 = mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r][0];
- r1 = mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r][1];
- r2 = mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r][2];
- r3 = mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++][3];
- C0 = mcol((S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
- C1 = mcol((S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24)) ^ KW[r][1];
- C2 = mcol((S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24)) ^ KW[r][2];
- C3 = mcol((S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24)) ^ KW[r++][3];
+ r0 = mcol((S[t0&255]&255) ^ ((S[(t1>>8)&255]&255)<<8) ^ ((S[(t2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
+ r1 = mcol((S[t1&255]&255) ^ ((S[(t2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(t0>>24)&255]<<24)) ^ KW[r][1];
+ r2 = mcol((S[t2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(t0>>16)&255]&255)<<16) ^ (S[(t1>>24)&255]<<24)) ^ KW[r][2];
+ r3 = mcol((S[r3&255]&255) ^ ((S[(t0>>8)&255]&255)<<8) ^ ((S[(t1>>16)&255]&255)<<16) ^ (S[(t2>>24)&255]<<24)) ^ KW[r++][3];
+ t0 = mcol((S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
+ t1 = mcol((S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24)) ^ KW[r][1];
+ t2 = mcol((S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24)) ^ KW[r][2];
+ r3 = mcol((S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24)) ^ KW[r++][3];
}
- r0 = mcol((S[C0&255]&255) ^ ((S[(C1>>8)&255]&255)<<8) ^ ((S[(C2>>16)&255]&255)<<16) ^ (S[(C3>>24)&255]<<24)) ^ KW[r][0];
- r1 = mcol((S[C1&255]&255) ^ ((S[(C2>>8)&255]&255)<<8) ^ ((S[(C3>>16)&255]&255)<<16) ^ (S[(C0>>24)&255]<<24)) ^ KW[r][1];
- r2 = mcol((S[C2&255]&255) ^ ((S[(C3>>8)&255]&255)<<8) ^ ((S[(C0>>16)&255]&255)<<16) ^ (S[(C1>>24)&255]<<24)) ^ KW[r][2];
- r3 = mcol((S[C3&255]&255) ^ ((S[(C0>>8)&255]&255)<<8) ^ ((S[(C1>>16)&255]&255)<<16) ^ (S[(C2>>24)&255]<<24)) ^ KW[r++][3];
+ r0 = mcol((S[t0&255]&255) ^ ((S[(t1>>8)&255]&255)<<8) ^ ((S[(t2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24)) ^ KW[r][0];
+ r1 = mcol((S[t1&255]&255) ^ ((S[(t2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(t0>>24)&255]<<24)) ^ KW[r][1];
+ r2 = mcol((S[t2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(t0>>16)&255]&255)<<16) ^ (S[(t1>>24)&255]<<24)) ^ KW[r][2];
+ r3 = mcol((S[r3&255]&255) ^ ((S[(t0>>8)&255]&255)<<8) ^ ((S[(t1>>16)&255]&255)<<16) ^ (S[(t2>>24)&255]<<24)) ^ KW[r++][3];
// the final round is a simple function of S
- C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
- C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
- C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
- C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
-
+ this.C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
+ this.C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
+ this.C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
+ this.C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
}
private void decryptBlock(int[][] KW)
{
- int r, r0, r1, r2, r3;
-
- C0 ^= KW[ROUNDS][0];
- C1 ^= KW[ROUNDS][1];
- C2 ^= KW[ROUNDS][2];
- C3 ^= KW[ROUNDS][3];
+ int t0 = this.C0 ^ KW[ROUNDS][0];
+ int t1 = this.C1 ^ KW[ROUNDS][1];
+ int t2 = this.C2 ^ KW[ROUNDS][2];
- for (r = ROUNDS-1; r>1;)
+ int r = ROUNDS - 1, r0, r1, r2, r3 = this.C3 ^ KW[ROUNDS][3];
+ while (r > 1)
{
- r0 = inv_mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r][0];
- r1 = inv_mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r][1];
- r2 = inv_mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r][2];
- r3 = inv_mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r--][3];
- C0 = inv_mcol((Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24)) ^ KW[r][0];
- C1 = inv_mcol((Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24)) ^ KW[r][1];
- C2 = inv_mcol((Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
- C3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24)) ^ KW[r--][3];
+ r0 = inv_mcol((Si[t0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(t2>>16)&255]&255)<<16) ^ (Si[(t1>>24)&255]<<24)) ^ KW[r][0];
+ r1 = inv_mcol((Si[t1&255]&255) ^ ((Si[(t0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(t2>>24)&255]<<24)) ^ KW[r][1];
+ r2 = inv_mcol((Si[t2&255]&255) ^ ((Si[(t1>>8)&255]&255)<<8) ^ ((Si[(t0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
+ r3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(t2>>8)&255]&255)<<8) ^ ((Si[(t1>>16)&255]&255)<<16) ^ (Si[(t0>>24)&255]<<24)) ^ KW[r--][3];
+ t0 = inv_mcol((Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24)) ^ KW[r][0];
+ t1 = inv_mcol((Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24)) ^ KW[r][1];
+ t2 = inv_mcol((Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
+ r3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24)) ^ KW[r--][3];
}
- r0 = inv_mcol((Si[C0&255]&255) ^ ((Si[(C3>>8)&255]&255)<<8) ^ ((Si[(C2>>16)&255]&255)<<16) ^ (Si[(C1>>24)&255]<<24)) ^ KW[r][0];
- r1 = inv_mcol((Si[C1&255]&255) ^ ((Si[(C0>>8)&255]&255)<<8) ^ ((Si[(C3>>16)&255]&255)<<16) ^ (Si[(C2>>24)&255]<<24)) ^ KW[r][1];
- r2 = inv_mcol((Si[C2&255]&255) ^ ((Si[(C1>>8)&255]&255)<<8) ^ ((Si[(C0>>16)&255]&255)<<16) ^ (Si[(C3>>24)&255]<<24)) ^ KW[r][2];
- r3 = inv_mcol((Si[C3&255]&255) ^ ((Si[(C2>>8)&255]&255)<<8) ^ ((Si[(C1>>16)&255]&255)<<16) ^ (Si[(C0>>24)&255]<<24)) ^ KW[r][3];
+ r0 = inv_mcol((Si[t0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(t2>>16)&255]&255)<<16) ^ (Si[(t1>>24)&255]<<24)) ^ KW[r][0];
+ r1 = inv_mcol((Si[t1&255]&255) ^ ((Si[(t0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(t2>>24)&255]<<24)) ^ KW[r][1];
+ r2 = inv_mcol((Si[t2&255]&255) ^ ((Si[(t1>>8)&255]&255)<<8) ^ ((Si[(t0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24)) ^ KW[r][2];
+ r3 = inv_mcol((Si[r3&255]&255) ^ ((Si[(t2>>8)&255]&255)<<8) ^ ((Si[(t1>>16)&255]&255)<<16) ^ (Si[(t0>>24)&255]<<24)) ^ KW[r][3];
// the final round's table is a simple function of Si
- C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
- C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
- C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
- C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
+ this.C0 = (Si[r0&255]&255) ^ ((Si[(r3>>8)&255]&255)<<8) ^ ((Si[(r2>>16)&255]&255)<<16) ^ (Si[(r1>>24)&255]<<24) ^ KW[0][0];
+ this.C1 = (Si[r1&255]&255) ^ ((Si[(r0>>8)&255]&255)<<8) ^ ((Si[(r3>>16)&255]&255)<<16) ^ (Si[(r2>>24)&255]<<24) ^ KW[0][1];
+ this.C2 = (Si[r2&255]&255) ^ ((Si[(r1>>8)&255]&255)<<8) ^ ((Si[(r0>>16)&255]&255)<<16) ^ (Si[(r3>>24)&255]<<24) ^ KW[0][2];
+ this.C3 = (Si[r3&255]&255) ^ ((Si[(r2>>8)&255]&255)<<8) ^ ((Si[(r1>>16)&255]&255)<<16) ^ (Si[(r0>>24)&255]<<24) ^ KW[0][3];
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
index 5d316ac4..6d3e5ac2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapEngine.java
@@ -9,8 +9,21 @@ package org.bouncycastle.crypto.engines;
public class AESWrapEngine
extends RFC3394WrapEngine
{
+ /**
+ * Create a regular AESWrapEngine specifying the encrypt for wrapping, decrypt for unwrapping.
+ */
public AESWrapEngine()
{
super(new AESEngine());
}
+
+ /**
+ * Create an AESWrapEngine where the underlying cipher is set to decrypt for wrapping, encrypt for unwrapping.
+ *
+ * @param useReverseDirection true if underlying cipher should be used in decryption mode, false otherwise.
+ */
+ public AESWrapEngine(boolean useReverseDirection)
+ {
+ super(new AESEngine(), useReverseDirection);
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapPadEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapPadEngine.java
new file mode 100644
index 00000000..77760612
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/AESWrapPadEngine.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.crypto.engines;
+
+public class AESWrapPadEngine
+ extends RFC5649WrapEngine
+{
+ public AESWrapPadEngine()
+ {
+ super(new AESEngine());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
index 2d1de39c..d4c43897 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
@@ -1,13 +1,12 @@
package org.bouncycastle.crypto.engines;
-import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Pack;
/**
* Implementation of Daniel J. Bernstein's ChaCha stream cipher.
*/
public class ChaChaEngine extends Salsa20Engine
{
-
/**
* Creates a 20 rounds ChaCha engine.
*/
@@ -30,6 +29,26 @@ public class ChaChaEngine extends Salsa20Engine
return "ChaCha" + rounds;
}
+ protected void advanceCounter(long diff)
+ {
+ int hi = (int)(diff >>> 32);
+ int lo = (int)diff;
+
+ if (hi > 0)
+ {
+ engineState[13] += hi;
+ }
+
+ int oldState = engineState[12];
+
+ engineState[12] += lo;
+
+ if (oldState != 0 && engineState[12] < oldState)
+ {
+ engineState[13]++;
+ }
+ }
+
protected void advanceCounter()
{
if (++engineState[12] == 0)
@@ -38,48 +57,102 @@ public class ChaChaEngine extends Salsa20Engine
}
}
- protected void resetCounter()
+ protected void retreatCounter(long diff)
{
- engineState[12] = engineState[13] = 0;
+ int hi = (int)(diff >>> 32);
+ int lo = (int)diff;
+
+ if (hi != 0)
+ {
+ if ((engineState[13] & 0xffffffffL) >= (hi & 0xffffffffL))
+ {
+ engineState[13] -= hi;
+ }
+ else
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+ }
+
+ if ((engineState[12] & 0xffffffffL) >= (lo & 0xffffffffL))
+ {
+ engineState[12] -= lo;
+ }
+ else
+ {
+ if (engineState[13] != 0)
+ {
+ --engineState[13];
+ engineState[12] -= lo;
+ }
+ else
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+ }
}
- protected void setKey(byte[] keyBytes, byte[] ivBytes)
+ protected void retreatCounter()
{
- if ((keyBytes.length != 16) && (keyBytes.length != 32))
+ if (engineState[12] == 0 && engineState[13] == 0)
{
- throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+ throw new IllegalStateException("attempt to reduce counter past zero.");
}
- int offset = 0;
- byte[] constants;
+ if (--engineState[12] == -1)
+ {
+ --engineState[13];
+ }
+ }
- // 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);
+ protected long getCounter()
+ {
+ return ((long)engineState[13] << 32) | (engineState[12] & 0xffffffffL);
+ }
- if (keyBytes.length == 32)
- {
- constants = sigma;
- offset = 16;
- } else
+ protected void resetCounter()
+ {
+ engineState[12] = engineState[13] = 0;
+ }
+
+ protected void setKey(byte[] keyBytes, byte[] ivBytes)
+ {
+ if (keyBytes != null)
{
- constants = tau;
- }
+ if ((keyBytes.length != 16) && (keyBytes.length != 32))
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+ }
- 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);
+ // 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);
- engineState[0] = Pack.littleEndianToInt(constants, 0);
- engineState[1] = Pack.littleEndianToInt(constants, 4);
- engineState[2] = Pack.littleEndianToInt(constants, 8);
- engineState[3] = Pack.littleEndianToInt(constants, 12);
+ byte[] constants;
+ int offset;
+ if (keyBytes.length == 32)
+ {
+ constants = sigma;
+ offset = 16;
+ }
+ else
+ {
+ constants = tau;
+ offset = 0;
+ }
- // Counter
- engineState[12] = engineState[13] = 0;
+ 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);
+ }
// IV
engineState[14] = Pack.littleEndianToInt(ivBytes, 0);
@@ -96,18 +169,19 @@ public class ChaChaEngine extends Salsa20Engine
* ChacCha function
*
* @param input input data
- *
- * @return keystream
*/
public static void chachaCore(int rounds, int[] input, int[] x)
{
- if (input.length != 16) {
+ if (input.length != 16)
+ {
throw new IllegalArgumentException();
}
- if (x.length != 16) {
+ if (x.length != 16)
+ {
throw new IllegalArgumentException();
}
- if (rounds % 2 != 0) {
+ if (rounds % 2 != 0)
+ {
throw new IllegalArgumentException("Number of rounds must be even");
}
@@ -182,5 +256,4 @@ public class ChaChaEngine extends Salsa20Engine
x[14] = x14 + input[14];
x[15] = x15 + input[15];
}
-
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCiphertext.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCiphertext.java
new file mode 100644
index 00000000..5c4e6081
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCiphertext.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.crypto.engines;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+/**
+ * Class, holding Cramer Shoup ciphertexts (u1, u2, e, v)
+ */
+public class CramerShoupCiphertext
+{
+ BigInteger u1, u2, e, v;
+
+ public CramerShoupCiphertext()
+ {
+ }
+
+ public CramerShoupCiphertext(BigInteger u1, BigInteger u2, BigInteger e, BigInteger v)
+ {
+ this.u1 = u1;
+ this.u2 = u2;
+ this.e = e;
+ this.v = v;
+ }
+
+ public CramerShoupCiphertext(byte[] c)
+ {
+ int off = 0, s;
+ byte[] tmp;
+
+ s = Pack.bigEndianToInt(c, off);
+ off += 4;
+ tmp = Arrays.copyOfRange(c, off, off + s);
+ off += s;
+ u1 = new BigInteger(tmp);
+
+ s = Pack.bigEndianToInt(c, off);
+ off += 4;
+ tmp = Arrays.copyOfRange(c, off, off + s);
+ off += s;
+ u2 = new BigInteger(tmp);
+
+ s = Pack.bigEndianToInt(c, off);
+ off += 4;
+ tmp = Arrays.copyOfRange(c, off, off + s);
+ off += s;
+ e = new BigInteger(tmp);
+
+ s = Pack.bigEndianToInt(c, off);
+ off += 4;
+ tmp = Arrays.copyOfRange(c, off, off + s);
+ off += s;
+ v = new BigInteger(tmp);
+ }
+
+ public BigInteger getU1()
+ {
+ return u1;
+ }
+
+ public void setU1(BigInteger u1)
+ {
+ this.u1 = u1;
+ }
+
+ public BigInteger getU2()
+ {
+ return u2;
+ }
+
+ public void setU2(BigInteger u2)
+ {
+ this.u2 = u2;
+ }
+
+ public BigInteger getE()
+ {
+ return e;
+ }
+
+ public void setE(BigInteger e)
+ {
+ this.e = e;
+ }
+
+ public BigInteger getV()
+ {
+ return v;
+ }
+
+ public void setV(BigInteger v)
+ {
+ this.v = v;
+ }
+
+ public String toString()
+ {
+ StringBuffer result = new StringBuffer();
+
+ result.append("u1: " + u1.toString());
+ result.append("\nu2: " + u2.toString());
+ result.append("\ne: " + e.toString());
+ result.append("\nv: " + v.toString());
+
+ return result.toString();
+ }
+
+ /**
+ * convert the cipher-text in a byte array,
+ * prepending them with 4 Bytes for their length
+ *
+ * @return
+ */
+ public byte[] toByteArray()
+ {
+ byte[] u1Bytes = u1.toByteArray();
+ int u1Length = u1Bytes.length;
+ byte[] u2Bytes = u2.toByteArray();
+ int u2Length = u2Bytes.length;
+ byte[] eBytes = e.toByteArray();
+ int eLength = eBytes.length;
+ byte[] vBytes = v.toByteArray();
+ int vLength = vBytes.length;
+
+ int off = 0;
+ byte[] result = new byte[u1Length + u2Length + eLength + vLength + 4 * 4];
+ Pack.intToBigEndian(u1Length, result, off);
+ off += 4;
+ System.arraycopy(u1Bytes, 0, result, off, u1Length);
+ off += u1Length;
+ Pack.intToBigEndian(u2Length, result, off);
+ off += 4;
+ System.arraycopy(u2Bytes, 0, result, off, u2Length);
+ off += u2Length;
+ Pack.intToBigEndian(eLength, result, off);
+ off += 4;
+ System.arraycopy(eBytes, 0, result, off, eLength);
+ off += eLength;
+ Pack.intToBigEndian(vLength, result, off);
+ off += 4;
+ System.arraycopy(vBytes, 0, result, off, vLength);
+ off += vLength;
+
+ return result;
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCoreEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCoreEngine.java
new file mode 100644
index 00000000..5102f152
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/CramerShoupCoreEngine.java
@@ -0,0 +1,309 @@
+package org.bouncycastle.crypto.engines;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.params.CramerShoupKeyParameters;
+import org.bouncycastle.crypto.params.CramerShoupPrivateKeyParameters;
+import org.bouncycastle.crypto.params.CramerShoupPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * Essentially the Cramer-Shoup encryption / decryption algorithms according to
+ * "A practical public key cryptosystem provably secure against adaptive chosen ciphertext attack." (Crypto 1998)
+ */
+public class CramerShoupCoreEngine
+{
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private CramerShoupKeyParameters key;
+ private SecureRandom random;
+ private boolean forEncryption;
+ private String label = null;
+
+ /**
+ * initialise the CramerShoup engine.
+ *
+ * @param forEncryption whether this engine should encrypt or decrypt
+ * @param param the necessary CramerShoup key parameters.
+ * @param label the label for labelled CS as {@link String}
+ */
+ public void init(boolean forEncryption, CipherParameters param, String label)
+ {
+ init(forEncryption, param);
+
+ this.label = label;
+ }
+
+ /**
+ * initialise the CramerShoup engine.
+ *
+ * @param forEncryption whether this engine should encrypt or decrypt
+ * @param param the necessary CramerShoup key parameters.
+ */
+ public void init(boolean forEncryption, CipherParameters param)
+ {
+ SecureRandom providedRandom = null;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ key = (CramerShoupKeyParameters)rParam.getParameters();
+ providedRandom = rParam.getRandom();
+ }
+ else
+ {
+ key = (CramerShoupKeyParameters)param;
+ }
+
+ this.random = initSecureRandom(forEncryption, providedRandom);
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine. For Cramer
+ * Shoup this is always one byte less than the key size on encryption, and
+ * the same length as the key size on decryption.
+ * TODO: correct?
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ int bitSize = key.getParameters().getP().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ else
+ {
+ return (bitSize + 7) / 8;
+ }
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine. For Cramer
+ * Shoup this is always one byte less than the key size on decryption, and
+ * the same length as the key size on encryption.
+ * TODO: correct?
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ int bitSize = key.getParameters().getP().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8;
+ }
+ else
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ }
+
+ public BigInteger convertInput(byte[] in, int inOff, int inLen)
+ {
+ if (inLen > (getInputBlockSize() + 1))
+ {
+ throw new DataLengthException("input too large for Cramer Shoup cipher.");
+ }
+ else if (inLen == (getInputBlockSize() + 1) && forEncryption)
+ {
+ throw new DataLengthException("input too large for Cramer Shoup cipher.");
+ }
+
+ byte[] block;
+
+ if (inOff != 0 || inLen != in.length)
+ {
+ block = new byte[inLen];
+
+ System.arraycopy(in, inOff, block, 0, inLen);
+ }
+ else
+ {
+ block = in;
+ }
+
+ BigInteger res = new BigInteger(1, block);
+ if (res.compareTo(key.getParameters().getP()) >= 0)
+ {
+ throw new DataLengthException("input too large for Cramer Shoup cipher.");
+ }
+
+ return res;
+ }
+
+ public byte[] convertOutput(BigInteger result)
+ {
+ byte[] output = result.toByteArray();
+
+ if (!forEncryption)
+ {
+ if (output[0] == 0 && output.length > getOutputBlockSize())
+ { // have ended up with an extra zero byte, copy down.
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ if (output.length < getOutputBlockSize())
+ {// have ended up with less bytes than normal, lengthen
+ byte[] tmp = new byte[getOutputBlockSize()];
+
+ System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
+
+ return tmp;
+ }
+ }
+ else
+ {
+ if (output[0] == 0)
+ { // have ended up with an extra zero byte, copy down.
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+ }
+
+ return output;
+ }
+
+ public CramerShoupCiphertext encryptBlock(BigInteger input)
+ {
+
+ CramerShoupCiphertext result = null;
+
+ if (!key.isPrivate() && this.forEncryption && key instanceof CramerShoupPublicKeyParameters)
+ {
+ CramerShoupPublicKeyParameters pk = (CramerShoupPublicKeyParameters)key;
+ BigInteger p = pk.getParameters().getP();
+ BigInteger g1 = pk.getParameters().getG1();
+ BigInteger g2 = pk.getParameters().getG2();
+
+ BigInteger h = pk.getH();
+
+ if (!isValidMessage(input, p))
+ {
+ return result;
+ }
+
+ BigInteger r = generateRandomElement(p, random);
+
+ BigInteger u1, u2, v, e, a;
+
+ u1 = g1.modPow(r, p);
+ u2 = g2.modPow(r, p);
+ e = h.modPow(r, p).multiply(input).mod(p);
+
+ Digest digest = pk.getParameters().getH();
+ byte[] u1Bytes = u1.toByteArray();
+ digest.update(u1Bytes, 0, u1Bytes.length);
+ byte[] u2Bytes = u2.toByteArray();
+ digest.update(u2Bytes, 0, u2Bytes.length);
+ byte[] eBytes = e.toByteArray();
+ digest.update(eBytes, 0, eBytes.length);
+ if (this.label != null)
+ {
+ byte[] lBytes = this.label.getBytes();
+ digest.update(lBytes, 0, lBytes.length);
+ }
+ byte[] out = new byte[digest.getDigestSize()];
+ digest.doFinal(out, 0);
+ a = new BigInteger(1, out);
+
+ v = pk.getC().modPow(r, p).multiply(pk.getD().modPow(r.multiply(a), p)).mod(p);
+
+ result = new CramerShoupCiphertext(u1, u2, e, v);
+ }
+ return result;
+ }
+
+ public BigInteger decryptBlock(CramerShoupCiphertext input)
+ throws CramerShoupCiphertextException
+ {
+
+ BigInteger result = null;
+
+ if (key.isPrivate() && !this.forEncryption && key instanceof CramerShoupPrivateKeyParameters)
+ {
+ CramerShoupPrivateKeyParameters sk = (CramerShoupPrivateKeyParameters)key;
+
+ BigInteger p = sk.getParameters().getP();
+
+ Digest digest = sk.getParameters().getH();
+ byte[] u1Bytes = input.getU1().toByteArray();
+ digest.update(u1Bytes, 0, u1Bytes.length);
+ byte[] u2Bytes = input.getU2().toByteArray();
+ digest.update(u2Bytes, 0, u2Bytes.length);
+ byte[] eBytes = input.getE().toByteArray();
+ digest.update(eBytes, 0, eBytes.length);
+ if (this.label != null)
+ {
+ byte[] lBytes = this.label.getBytes();
+ digest.update(lBytes, 0, lBytes.length);
+ }
+ byte[] out = new byte[digest.getDigestSize()];
+ digest.doFinal(out, 0);
+
+ BigInteger a = new BigInteger(1, out);
+ BigInteger v = input.u1.modPow(sk.getX1().add(sk.getY1().multiply(a)), p).
+ multiply(input.u2.modPow(sk.getX2().add(sk.getY2().multiply(a)), p)).mod(p);
+
+ // check correctness of ciphertext
+ if (input.v.equals(v))
+ {
+ result = input.e.multiply(input.u1.modPow(sk.getZ(), p).modInverse(p)).mod(p);
+ }
+ else
+ {
+ throw new CramerShoupCiphertextException("Sorry, that ciphertext is not correct");
+ }
+ }
+ return result;
+ }
+
+ private BigInteger generateRandomElement(BigInteger p, SecureRandom random)
+ {
+ return BigIntegers.createRandomInRange(ONE, p.subtract(ONE), random);
+ }
+
+ /**
+ * just checking whether the message m is actually less than the group order p
+ */
+ private boolean isValidMessage(BigInteger m, BigInteger p)
+ {
+ return m.compareTo(p) < 0;
+ }
+
+ protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
+ {
+ return !needed ? null : (provided != null) ? provided : new SecureRandom();
+ }
+
+ /**
+ * CS exception for wrong cipher-texts
+ */
+ public static class CramerShoupCiphertextException
+ extends Exception
+ {
+ private static final long serialVersionUID = -6360977166495345076L;
+
+ public CramerShoupCiphertextException(String msg)
+ {
+ super(msg);
+ }
+
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
index 9b1e404e..6980fd08 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESEngine.java
@@ -301,7 +301,7 @@ public class DESEngine
* generate an integer based working key based on our secret key
* and what we processing we are planning to do.
*
- * Acknowledgements for this routine go to James Gillogly & Phil Karn.
+ * Acknowledgements for this routine go to James Gillogly &amp; Phil Karn.
* (whoever, and wherever they are!).
*/
protected int[] generateWorkingKey(
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
index a3c72ccb..923a79cd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java
@@ -58,8 +58,8 @@ public class DESedeWrapEngine
/**
* Method init
*
- * @param forWrapping
- * @param param
+ * @param forWrapping true if for wrapping, false otherwise.
+ * @param param necessary parameters, may include KeyParameter, ParametersWithRandom, and ParametersWithIV
*/
public void init(boolean forWrapping, CipherParameters param)
{
@@ -128,9 +128,9 @@ public class DESedeWrapEngine
/**
* Method wrap
*
- * @param in
- * @param inOff
- * @param inLen
+ * @param in byte array containing the encoded key.
+ * @param inOff off set into in that the data starts at.
+ * @param inLen length of the data.
* @return the wrapped bytes.
*/
public byte[] wrap(byte[] in, int inOff, int inLen)
@@ -199,9 +199,9 @@ public class DESedeWrapEngine
/**
* Method unwrap
*
- * @param in
- * @param inOff
- * @param inLen
+ * @param in byte array containing the wrapped key.
+ * @param inOff off set into in that the data starts at.
+ * @param inLen length of the data.
* @return the unwrapped bytes.
* @throws InvalidCipherTextException
*/
@@ -305,10 +305,11 @@ public class DESedeWrapEngine
* - Compute the 20 octet SHA-1 hash on the key being wrapped.
* - Use the first 8 octets of this hash as the checksum value.
*
- * @param key
+ * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum.
+ *
+ * @param key the key to check,
* @return the CMS checksum.
* @throws RuntimeException
- * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
*/
private byte[] calculateCMSKeyChecksum(
byte[] key)
@@ -324,10 +325,11 @@ public class DESedeWrapEngine
}
/**
- * @param key
- * @param checksum
+ * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ *
+ * @param key key to be validated.
+ * @param checksum the checksum.
* @return true if okay, false otherwise.
- * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
*/
private boolean checkCMSKeyChecksum(
byte[] key,
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java
index 4bf8e75d..ef8e799d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ElGamalEngine.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;
@@ -9,9 +12,6 @@ import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.util.BigIntegers;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
/**
* this does your basic ElGamal algorithm.
*/
@@ -170,7 +170,7 @@ public class ElGamalEngine
BigInteger input = new BigInteger(1, block);
- if (input.bitLength() >= p.bitLength())
+ if (input.compareTo(p) >= 0)
{
throw new DataLengthException("input too large for ElGamal cipher.\n");
}
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 89271f0d..f5ecb75c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
@@ -232,7 +232,7 @@ public class Grain128Engine
}
}
- public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
int outOff)
throws DataLengthException
{
@@ -256,6 +256,8 @@ public class Grain128Engine
{
out[outOff + i] = (byte)(in[inOff + i] ^ getKeyStream());
}
+
+ return len;
}
public void 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 782a93ce..77f52d1c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
@@ -220,7 +220,7 @@ public class Grainv1Engine
}
}
- public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
int outOff)
throws DataLengthException
{
@@ -244,6 +244,8 @@ public class Grainv1Engine
{
out[outOff + i] = (byte)(in[inOff + i] ^ getKeyStream());
}
+
+ return len;
}
public void 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 015e49e4..7d18d623 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
@@ -220,7 +220,7 @@ public class HC128Engine
return ret;
}
- public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
int outOff) throws DataLengthException
{
if (!initialised)
@@ -243,6 +243,8 @@ public class HC128Engine
{
out[outOff + i] = (byte)(in[inOff + i] ^ getByte());
}
+
+ return len;
}
public void reset()
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 8bd7e9da..a74164ae 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
@@ -200,7 +200,7 @@ public class HC256Engine
return ret;
}
- public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
int outOff) throws DataLengthException
{
if (!initialised)
@@ -223,6 +223,8 @@ public class HC256Engine
{
out[outOff + i] = (byte)(in[inOff + i] ^ getByte());
}
+
+ return len;
}
public void reset()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java
index fdf3f6d3..08cf738e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/IDEAEngine.java
@@ -10,22 +10,12 @@ import org.bouncycastle.crypto.params.KeyParameter;
* A class that provides a basic International Data Encryption Algorithm (IDEA) engine.
* <p>
* This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM"
- * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (baring 1 typo at the
+ * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (barring 1 typo at the
* end of the mulinv function!).
* <p>
* It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/
* <p>
- * Note 1: This algorithm is patented in the USA, Japan, and Europe including
- * at least Austria, France, Germany, Italy, Netherlands, Spain, Sweden, Switzerland
- * and the United Kingdom. Non-commercial use is free, however any commercial
- * products are liable for royalties. Please see
- * <a href="http://www.mediacrypt.com">www.mediacrypt.com</a> for
- * further details. This announcement has been included at the request of
- * the patent holders.
- * <p>
- * Note 2: Due to the requests concerning the above, this algorithm is now only
- * included in the extended Bouncy Castle provider and JCE signed jars. It is
- * not included in the default distributions.
+ * Note: This algorithm was patented in the USA, Japan and Europe. These patents expired in 2011/2012.
*/
public class IDEAEngine
implements BlockCipher
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
index ea8556de..d2dee1ce 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java
@@ -18,9 +18,10 @@ import org.bouncycastle.crypto.params.IESParameters;
import org.bouncycastle.crypto.params.IESWithCipherParameters;
import org.bouncycastle.crypto.params.KDFParameters;
import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Pack;
/**
* Support class for constructing integrated encryption ciphers
@@ -42,7 +43,7 @@ public class IESEngine
byte[] V;
private EphemeralKeyPairGenerator keyPairGenerator;
private KeyParser keyParser;
-
+ private byte[] IV;
/**
* set up for use with stream mode, where the key derivation function
@@ -87,57 +88,72 @@ public class IESEngine
this.cipher = cipher;
}
-
/**
* Initialise the encryptor.
*
* @param forEncryption whether or not this is encryption/decryption.
* @param privParam our private key parameters
* @param pubParam the recipient's/sender's public key parameters
- * @param param encoding and derivation parameters.
+ * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher.
*/
public void init(
boolean forEncryption,
CipherParameters privParam,
CipherParameters pubParam,
- CipherParameters param)
+ CipherParameters params)
{
this.forEncryption = forEncryption;
this.privParam = privParam;
this.pubParam = pubParam;
- this.param = (IESParameters)param;
this.V = new byte[0];
- }
+ extractParams(params);
+ }
/**
- * Initialise the encryptor.
+ * Initialise the decryptor.
*
* @param publicKey the recipient's/sender's public key parameters
- * @param params encoding and derivation parameters.
+ * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher.
* @param ephemeralKeyPairGenerator the ephemeral key pair generator to use.
*/
public void init(AsymmetricKeyParameter publicKey, CipherParameters params, EphemeralKeyPairGenerator ephemeralKeyPairGenerator)
{
this.forEncryption = true;
this.pubParam = publicKey;
- this.param = (IESParameters)params;
this.keyPairGenerator = ephemeralKeyPairGenerator;
+
+ extractParams(params);
}
/**
* Initialise the encryptor.
*
* @param privateKey the recipient's private key.
- * @param params encoding and derivation parameters.
+ * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher.
* @param publicKeyParser the parser for reading the ephemeral public key.
*/
public void init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser)
{
this.forEncryption = false;
this.privParam = privateKey;
- this.param = (IESParameters)params;
this.keyParser = publicKeyParser;
+
+ extractParams(params);
+ }
+
+ private void extractParams(CipherParameters params)
+ {
+ if (params instanceof ParametersWithIV)
+ {
+ this.IV = ((ParametersWithIV)params).getIV();
+ this.param = (IESParameters)((ParametersWithIV)params).getParameters();
+ }
+ else
+ {
+ this.IV = null;
+ this.param = (IESParameters)params;
+ }
}
public BufferedBlockCipher getCipher()
@@ -198,7 +214,16 @@ public class IESEngine
System.arraycopy(K, 0, K1, 0, K1.length);
System.arraycopy(K, K1.length, K2, 0, K2.length);
- cipher.init(true, new KeyParameter(K1));
+ // If iv provided use it to initialise the cipher
+ if (IV != null)
+ {
+ cipher.init(true, new ParametersWithIV(new KeyParameter(K1), IV));
+ }
+ else
+ {
+ cipher.init(true, new KeyParameter(K1));
+ }
+
C = new byte[cipher.getOutputSize(inLen)];
len = cipher.processBytes(in, inOff, inLen, C, 0);
len += cipher.doFinal(C, len);
@@ -247,6 +272,12 @@ public class IESEngine
byte[] M = null, K = null, K1 = null, K2 = null;
int len;
+ // Ensure that the length of the input is greater than the MAC in bytes
+ if (inLen <= (param.getMacKeySize() / 8))
+ {
+ throw new InvalidCipherTextException("Length of input must be greater than the MAC");
+ }
+
if (cipher == null)
{
// Streaming mode.
@@ -287,7 +318,15 @@ public class IESEngine
System.arraycopy(K, 0, K1, 0, K1.length);
System.arraycopy(K, K1.length, K2, 0, K2.length);
- cipher.init(false, new KeyParameter(K1));
+ // If IV provide use it to initialize the cipher
+ if (IV != null)
+ {
+ cipher.init(false, new ParametersWithIV(new KeyParameter(K1), IV));
+ }
+ else
+ {
+ cipher.init(false, new KeyParameter(K1));
+ }
M = new byte[cipher.getOutputSize(inLen - V.length - mac.getMacSize())];
len = cipher.processBytes(in_enc, inOff + V.length, inLen - V.length - mac.getMacSize(), M, 0);
@@ -327,7 +366,6 @@ public class IESEngine
throw new InvalidCipherTextException("Invalid MAC.");
}
-
// Output the message.
return Arrays.copyOfRange(M, 0, len);
}
@@ -375,24 +413,26 @@ public class IESEngine
byte[] Z = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), z);
// Create input to KDF.
- byte[] VZ;
if (V.length != 0)
{
- VZ = new byte[V.length + Z.length];
- System.arraycopy(V, 0, VZ, 0, V.length);
- System.arraycopy(Z, 0, VZ, V.length, Z.length);
- }
- else
- {
- VZ = Z;
+ byte[] VZ = Arrays.concatenate(V, Z);
+ Arrays.fill(Z, (byte)0);
+ Z = VZ;
}
- // Initialise the KDF.
- KDFParameters kdfParam = new KDFParameters(VZ, param.getDerivationV());
- kdf.init(kdfParam);
+ try
+ {
+ // Initialise the KDF.
+ KDFParameters kdfParam = new KDFParameters(Z, param.getDerivationV());
+ kdf.init(kdfParam);
- return forEncryption
- ? encryptBlock(in, inOff, inLen)
- : decryptBlock(in, inOff, inLen);
+ return forEncryption
+ ? encryptBlock(in, inOff, inLen)
+ : decryptBlock(in, inOff, inLen);
+ }
+ finally
+ {
+ Arrays.fill(Z, (byte)0);
+ }
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java
index d6e1ae11..d2dd2657 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ISAACEngine.java
@@ -5,7 +5,7 @@ import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Pack;
/**
* Implementation of Bob Jenkin's ISAAC (Indirection Shift Accumulate Add and Count).
@@ -69,7 +69,7 @@ public class ISAACEngine
return out;
}
- public void processBytes(
+ public int processBytes(
byte[] in,
int inOff,
int len,
@@ -101,6 +101,8 @@ public class ISAACEngine
out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]);
index = (index + 1) & 1023;
}
+
+ return len;
}
public String getAlgorithmName()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java
index 185387d8..ec82b1aa 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java
@@ -350,10 +350,11 @@ public class RC2WrapEngine
* - Compute the 20 octet SHA-1 hash on the key being wrapped.
* - Use the first 8 octets of this hash as the checksum value.
*
+ * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
* @param key
* @return
* @throws RuntimeException
- * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ *
*/
private byte[] calculateCMSKeyChecksum(
byte[] key)
@@ -369,10 +370,11 @@ public class RC2WrapEngine
}
/**
+ * For details see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
+ *
* @param key
* @param checksum
* @return
- * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
*/
private boolean checkCMSKeyChecksum(
byte[] key,
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
index 4de7ea69..c1ceaa4a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RC4Engine.java
@@ -68,7 +68,7 @@ public class RC4Engine implements StreamCipher
return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
}
- public void processBytes(
+ public int processBytes(
byte[] in,
int inOff,
int len,
@@ -99,6 +99,8 @@ public class RC4Engine implements StreamCipher
out[i+outOff] = (byte)(in[i + inOff]
^ engineState[(engineState[x] + engineState[y]) & 0xff]);
}
+
+ return len;
}
public void reset()
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 cfd86fb6..d2886e72 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
@@ -21,6 +21,7 @@ public class RFC3394WrapEngine
implements Wrapper
{
private BlockCipher engine;
+ private boolean wrapCipherMode;
private KeyParameter param;
private boolean forWrapping;
@@ -28,9 +29,26 @@ public class RFC3394WrapEngine
(byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
(byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
+ /**
+ * Create a RFC 3394 WrapEngine specifying the encrypt for wrapping, decrypt for unwrapping.
+ *
+ * @param engine the block cipher to be used for wrapping.
+ */
public RFC3394WrapEngine(BlockCipher engine)
{
+ this(engine, false);
+ }
+
+ /**
+ * Create a RFC 3394 WrapEngine specifying the direction for wrapping and unwrapping..
+ *
+ * @param engine the block cipher to be used for wrapping.
+ * @param useReverseDirection true if engine should be used in decryption mode for wrapping, false otherwise.
+ */
+ public RFC3394WrapEngine(BlockCipher engine, boolean useReverseDirection)
+ {
this.engine = engine;
+ this.wrapCipherMode = (useReverseDirection) ? false : true;
}
public void init(
@@ -87,7 +105,7 @@ public class RFC3394WrapEngine
System.arraycopy(iv, 0, block, 0, iv.length);
System.arraycopy(in, inOff, block, iv.length, inLen);
- engine.init(true, param);
+ engine.init(wrapCipherMode, param);
for (int j = 0; j != 6; j++)
{
@@ -140,7 +158,7 @@ public class RFC3394WrapEngine
System.arraycopy(in, inOff, a, 0, iv.length);
System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
- engine.init(false, param);
+ engine.init(!wrapCipherMode, param);
n = n - 1;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC5649WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC5649WrapEngine.java
new file mode 100644
index 00000000..4506ac75
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC5649WrapEngine.java
@@ -0,0 +1,300 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+/**
+ * An implementation of the AES Key Wrap with Padding specification
+ * as described in RFC 5649.
+ * <p>
+ * For details on the specification see:
+ * <a href="https://tools.ietf.org/html/rfc5649">https://tools.ietf.org/html/rfc5649</a>
+ * </p>
+ */
+public class RFC5649WrapEngine
+ implements Wrapper
+{
+ private BlockCipher engine;
+ private KeyParameter param;
+ private boolean forWrapping;
+
+ // The AIV as defined in the RFC
+ private byte[] highOrderIV = {(byte)0xa6, (byte)0x59, (byte)0x59, (byte)0xa6};
+ private byte[] preIV = highOrderIV;
+
+ private byte[] extractedAIV = null;
+
+ public RFC5649WrapEngine(BlockCipher engine)
+ {
+ this.engine = engine;
+ }
+
+ public void init(boolean forWrapping, CipherParameters param)
+ {
+ this.forWrapping = forWrapping;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ param = ((ParametersWithRandom)param).getParameters();
+ }
+
+ if (param instanceof KeyParameter)
+ {
+ this.param = (KeyParameter)param;
+ }
+ else if (param instanceof ParametersWithIV)
+ {
+ this.preIV = ((ParametersWithIV)param).getIV();
+ this.param = (KeyParameter)((ParametersWithIV)param).getParameters();
+ if (this.preIV.length != 4)
+ {
+ throw new IllegalArgumentException("IV length not equal to 4");
+ }
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return engine.getAlgorithmName();
+ }
+
+ /**
+ * Pads the plaintext (i.e., the key to be wrapped)
+ * as per section 4.1 of RFC 5649.
+ *
+ * @param plaintext The key being wrapped.
+ * @return The padded key.
+ */
+ private byte[] padPlaintext(byte[] plaintext)
+ {
+ int plaintextLength = plaintext.length;
+ int numOfZerosToAppend = (8 - (plaintextLength % 8)) % 8;
+ byte[] paddedPlaintext = new byte[plaintextLength + numOfZerosToAppend];
+ System.arraycopy(plaintext, 0, paddedPlaintext, 0, plaintextLength);
+ if (numOfZerosToAppend != 0)
+ {
+ // plaintext (i.e., key to be wrapped) does not have
+ // a multiple of 8 octet blocks so it must be padded
+ byte[] zeros = new byte[numOfZerosToAppend];
+ System.arraycopy(zeros, 0, paddedPlaintext, plaintextLength, numOfZerosToAppend);
+ }
+ return paddedPlaintext;
+ }
+
+ public byte[] wrap(byte[] in, int inOff, int inLen)
+ {
+ if (!forWrapping)
+ {
+ throw new IllegalStateException("not set for wrapping");
+ }
+ byte[] iv = new byte[8];
+
+ // MLI = size of key to be wrapped
+ byte[] mli = Pack.intToBigEndian(inLen);
+ // copy in the fixed portion of the AIV
+ System.arraycopy(preIV, 0, iv, 0, preIV.length);
+ // copy in the MLI after the AIV
+ System.arraycopy(mli, 0, iv, preIV.length, mli.length);
+
+ // get the relevant plaintext to be wrapped
+ byte[] relevantPlaintext = new byte[inLen];
+ System.arraycopy(in, inOff, relevantPlaintext, 0, inLen);
+ byte[] paddedPlaintext = padPlaintext(relevantPlaintext);
+
+ if (paddedPlaintext.length == 8)
+ {
+ // if the padded plaintext contains exactly 8 octets,
+ // then prepend iv and encrypt using AES in ECB mode.
+
+ // prepend the IV to the plaintext
+ byte[] paddedPlainTextWithIV = new byte[paddedPlaintext.length + iv.length];
+ System.arraycopy(iv, 0, paddedPlainTextWithIV, 0, iv.length);
+ System.arraycopy(paddedPlaintext, 0, paddedPlainTextWithIV, iv.length, paddedPlaintext.length);
+
+ engine.init(true, param);
+ for (int i = 0; i < paddedPlainTextWithIV.length; i += engine.getBlockSize())
+ {
+ engine.processBlock(paddedPlainTextWithIV, i, paddedPlainTextWithIV, i);
+ }
+
+ return paddedPlainTextWithIV;
+ }
+ else
+ {
+ // otherwise, apply the RFC 3394 wrap to
+ // the padded plaintext with the new IV
+ Wrapper wrapper = new RFC3394WrapEngine(engine);
+ ParametersWithIV paramsWithIV = new ParametersWithIV(param, iv);
+ wrapper.init(true, paramsWithIV);
+ return wrapper.wrap(paddedPlaintext, inOff, paddedPlaintext.length);
+ }
+
+ }
+
+ public byte[] unwrap(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forWrapping)
+ {
+ throw new IllegalStateException("not set for unwrapping");
+ }
+
+ int n = inLen / 8;
+
+ if ((n * 8) != inLen)
+ {
+ throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
+ }
+
+ if (n == 1)
+ {
+ throw new InvalidCipherTextException("unwrap data must be at least 16 bytes");
+ }
+
+ byte[] relevantCiphertext = new byte[inLen];
+ System.arraycopy(in, inOff, relevantCiphertext, 0, inLen);
+ byte[] decrypted = new byte[inLen];
+ byte[] paddedPlaintext;
+
+ if (n == 2)
+ {
+ // When there are exactly two 64-bit blocks of ciphertext,
+ // they are decrypted as a single block using AES in ECB.
+ engine.init(false, param);
+ for (int i = 0; i < relevantCiphertext.length; i += engine.getBlockSize())
+ {
+ engine.processBlock(relevantCiphertext, i, decrypted, i);
+ }
+
+ // extract the AIV
+ extractedAIV = new byte[8];
+ System.arraycopy(decrypted, 0, extractedAIV, 0, extractedAIV.length);
+ paddedPlaintext = new byte[decrypted.length - extractedAIV.length];
+ System.arraycopy(decrypted, extractedAIV.length, paddedPlaintext, 0, paddedPlaintext.length);
+ }
+ else
+ {
+ // Otherwise, unwrap as per RFC 3394 but don't check IV the same way
+ decrypted = rfc3394UnwrapNoIvCheck(in, inOff, inLen);
+ paddedPlaintext = decrypted;
+ }
+
+ // Decompose the extracted AIV to the fixed portion and the MLI
+ byte[] extractedHighOrderAIV = new byte[4];
+ byte[] mliBytes = new byte[4];
+ System.arraycopy(extractedAIV, 0, extractedHighOrderAIV, 0, extractedHighOrderAIV.length);
+ System.arraycopy(extractedAIV, extractedHighOrderAIV.length, mliBytes, 0, mliBytes.length);
+ int mli = Pack.bigEndianToInt(mliBytes, 0);
+
+ // Even if a check fails we still continue and check everything
+ // else in order to avoid certain timing based side-channel attacks.
+ boolean isValid = true;
+
+ // Check the fixed portion of the AIV
+ if (!Arrays.constantTimeAreEqual(extractedHighOrderAIV, preIV))
+ {
+ isValid = false;
+ }
+
+ // Check the MLI against the actual length
+ int upperBound = paddedPlaintext.length;
+ int lowerBound = upperBound - 8;
+ if (mli <= lowerBound)
+ {
+ isValid = false;
+ }
+ if (mli > upperBound)
+ {
+ isValid = false;
+ }
+
+ // Check the number of padded zeros
+ int expectedZeros = upperBound - mli;
+ if (expectedZeros >= paddedPlaintext.length)
+ {
+ isValid = false;
+ expectedZeros = paddedPlaintext.length;
+ }
+
+ byte[] zeros = new byte[expectedZeros];
+ byte[] pad = new byte[expectedZeros];
+ System.arraycopy(paddedPlaintext, paddedPlaintext.length - expectedZeros, pad, 0, expectedZeros);
+ if (!Arrays.constantTimeAreEqual(pad, zeros))
+ {
+ isValid = false;
+ }
+
+ if (!isValid)
+ {
+ throw new InvalidCipherTextException("checksum failed");
+ }
+
+ // Extract the plaintext from the padded plaintext
+ byte[] plaintext = new byte[mli];
+ System.arraycopy(paddedPlaintext, 0, plaintext, 0, plaintext.length);
+
+ return plaintext;
+ }
+
+ /**
+ * Performs steps 1 and 2 of the unwrap process defined in RFC 3394.
+ * This code is duplicated from RFC3394WrapEngine because that class
+ * will throw an error during unwrap because the IV won't match up.
+ *
+ * @param in
+ * @param inOff
+ * @param inLen
+ * @return Unwrapped data.
+ */
+ private byte[] rfc3394UnwrapNoIvCheck(byte[] in, int inOff, int inLen)
+ {
+ byte[] iv = new byte[8];
+ byte[] block = new byte[inLen - iv.length];
+ byte[] a = new byte[iv.length];
+ byte[] buf = new byte[8 + 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);
+
+ int n = inLen / 8;
+ n = n - 1;
+
+ for (int j = 5; j >= 0; j--)
+ {
+ for (int i = n; i >= 1; i--)
+ {
+ System.arraycopy(a, 0, buf, 0, iv.length);
+ System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
+
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
+
+ buf[iv.length - k] ^= v;
+
+ t >>>= 8;
+ }
+
+ engine.processBlock(buf, 0, buf, 0);
+ System.arraycopy(buf, 0, a, 0, 8);
+ System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
+ }
+ }
+
+ // set the extracted AIV
+ extractedAIV = a;
+
+ return block;
+ }
+
+}
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 2d6140da..13aada9e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
@@ -4,17 +4,17 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.MaxBytesExceededException;
import org.bouncycastle.crypto.OutputLengthException;
-import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.SkippingStreamCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
-import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Pack;
import org.bouncycastle.util.Strings;
/**
* Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
*/
public class Salsa20Engine
- implements StreamCipher
+ implements SkippingStreamCipher
{
public final static int DEFAULT_ROUNDS = 20;
@@ -33,7 +33,7 @@ public class Salsa20Engine
*/
private int index = 0;
protected int[] engineState = new int[STATE_SIZE]; // state
- protected int[] x = new int[STATE_SIZE] ; // internal buffer
+ 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;
@@ -96,15 +96,27 @@ public class Salsa20Engine
+ " bytes of IV");
}
- if (!(ivParams.getParameters() instanceof KeyParameter))
+ CipherParameters keyParam = ivParams.getParameters();
+ if (keyParam == null)
{
- throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include a key");
- }
+ if (!initialised)
+ {
+ throw new IllegalStateException(getAlgorithmName() + " KeyParameter can not be null for first initialisation");
+ }
- KeyParameter key = (KeyParameter) ivParams.getParameters();
+ setKey(null, iv);
+ }
+ else if (keyParam instanceof KeyParameter)
+ {
+ setKey(((KeyParameter)keyParam).getKey(), iv);
+ }
+ else
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must contain a KeyParameter (or null for re-init)");
+ }
- setKey(key.getKey(), iv);
reset();
+
initialised = true;
}
@@ -130,18 +142,38 @@ public class Salsa20Engine
throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV");
}
+ byte out = (byte)(keyStream[index]^in);
+ index = (index + 1) & 63;
+
if (index == 0)
{
- generateKeyStream(keyStream);
advanceCounter();
+ generateKeyStream(keyStream);
}
- byte out = (byte)(keyStream[index]^in);
- index = (index + 1) & 63;
-
return out;
}
+ protected void advanceCounter(long diff)
+ {
+ int hi = (int)(diff >>> 32);
+ int lo = (int)diff;
+
+ if (hi > 0)
+ {
+ engineState[9] += hi;
+ }
+
+ int oldState = engineState[8];
+
+ engineState[8] += lo;
+
+ if (oldState != 0 && engineState[8] < oldState)
+ {
+ engineState[9]++;
+ }
+ }
+
protected void advanceCounter()
{
if (++engineState[8] == 0)
@@ -150,7 +182,55 @@ public class Salsa20Engine
}
}
- public void processBytes(
+ protected void retreatCounter(long diff)
+ {
+ int hi = (int)(diff >>> 32);
+ int lo = (int)diff;
+
+ if (hi != 0)
+ {
+ if ((engineState[9] & 0xffffffffL) >= (hi & 0xffffffffL))
+ {
+ engineState[9] -= hi;
+ }
+ else
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+ }
+
+ if ((engineState[8] & 0xffffffffL) >= (lo & 0xffffffffL))
+ {
+ engineState[8] -= lo;
+ }
+ else
+ {
+ if (engineState[9] != 0)
+ {
+ --engineState[9];
+ engineState[8] -= lo;
+ }
+ else
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+ }
+ }
+
+ protected void retreatCounter()
+ {
+ if (engineState[8] == 0 && engineState[9] == 0)
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+
+ if (--engineState[8] == -1)
+ {
+ --engineState[9];
+ }
+ }
+
+ public int processBytes(
byte[] in,
int inOff,
int len,
@@ -179,15 +259,82 @@ public class Salsa20Engine
for (int i = 0; i < len; i++)
{
+ out[i + outOff] = (byte)(keyStream[index] ^ in[i + inOff]);
+ index = (index + 1) & 63;
+
if (index == 0)
{
+ advanceCounter();
generateKeyStream(keyStream);
+ }
+ }
+
+ return len;
+ }
+
+ public long skip(long numberOfBytes)
+ {
+ if (numberOfBytes >= 0)
+ {
+ long remaining = numberOfBytes;
+
+ if (remaining >= 64)
+ {
+ long count = remaining / 64;
+
+ advanceCounter(count);
+
+ remaining -= count * 64;
+ }
+
+ int oldIndex = index;
+
+ index = (index + (int)remaining) & 63;
+
+ if (index < oldIndex)
+ {
advanceCounter();
}
+ }
+ else
+ {
+ long remaining = -numberOfBytes;
- out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]);
- index = (index + 1) & 63;
+ if (remaining >= 64)
+ {
+ long count = remaining / 64;
+
+ retreatCounter(count);
+
+ remaining -= count * 64;
+ }
+
+ for (long i = 0; i < remaining; i++)
+ {
+ if (index == 0)
+ {
+ retreatCounter();
+ }
+
+ index = (index - 1) & 63;
+ }
}
+
+ generateKeyStream(keyStream);
+
+ return numberOfBytes;
+ }
+
+ public long seekTo(long position)
+ {
+ reset();
+
+ return skip(position);
+ }
+
+ public long getPosition()
+ {
+ return getCounter() * 64 + index;
}
public void reset()
@@ -195,6 +342,13 @@ public class Salsa20Engine
index = 0;
resetLimitCounter();
resetCounter();
+
+ generateKeyStream(keyStream);
+ }
+
+ protected long getCounter()
+ {
+ return ((long)engineState[9] << 32) | (engineState[8] & 0xffffffffL);
}
protected void resetCounter()
@@ -204,43 +358,46 @@ public class Salsa20Engine
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(keyBytes, 0);
- engineState[2] = Pack.littleEndianToInt(keyBytes, 4);
- engineState[3] = Pack.littleEndianToInt(keyBytes, 8);
- engineState[4] = Pack.littleEndianToInt(keyBytes, 12);
-
- if (keyBytes.length == 32)
- {
- constants = sigma;
- offset = 16;
- }
- else
+ if (keyBytes != null)
{
- constants = tau;
- }
+ if ((keyBytes.length != 16) && (keyBytes.length != 32))
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+ }
- 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);
+ // Key
+ engineState[1] = Pack.littleEndianToInt(keyBytes, 0);
+ engineState[2] = Pack.littleEndianToInt(keyBytes, 4);
+ engineState[3] = Pack.littleEndianToInt(keyBytes, 8);
+ engineState[4] = Pack.littleEndianToInt(keyBytes, 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);
+ byte[] constants;
+ int offset;
+ if (keyBytes.length == 32)
+ {
+ constants = sigma;
+ offset = 16;
+ }
+ else
+ {
+ constants = tau;
+ offset = 0;
+ }
+
+ 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(ivBytes, 0);
engineState[7] = Pack.littleEndianToInt(ivBytes, 4);
- resetCounter();
}
protected void generateKeyStream(byte[] output)
@@ -253,18 +410,19 @@ public class Salsa20Engine
* Salsa20 function
*
* @param input input data
- *
- * @return keystream
*/
public static void salsaCore(int rounds, int[] input, int[] x)
{
- if (input.length != 16) {
+ if (input.length != 16)
+ {
throw new IllegalArgumentException();
}
- if (x.length != 16) {
+ if (x.length != 16)
+ {
throw new IllegalArgumentException();
}
- if (rounds % 2 != 0) {
+ if (rounds % 2 != 0)
+ {
throw new IllegalArgumentException("Number of rounds must be even");
}
@@ -287,39 +445,39 @@ public class Salsa20Engine
for (int i = rounds; i > 0; i -= 2)
{
- 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);
+ 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);
}
x[ 0] = x00 + input[ 0];
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java
index 9da23013..8db5a6f7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngine.java
@@ -12,7 +12,7 @@ import org.bouncycastle.crypto.params.KeyParameter;
* secure as three-key triple-DES.
* <p>
* Serpent was designed by Ross Anderson, Eli Biham and Lars Knudsen as a
- * candidate algorithm for the NIST AES Quest.>
+ * candidate algorithm for the NIST AES Quest.
* <p>
* For full details see the <a href="http://www.cl.cam.ac.uk/~rja14/serpent.html">The Serpent home page</a>
*/
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java
index b59205de..62e05106 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java
@@ -5,6 +5,7 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
/**
* Block cipher Shacal2, designed by Helena Handschuh and David Naccache,
@@ -91,10 +92,10 @@ public class Shacal2Engine
}
}
- public void encryptBlock(byte[] in, int inOffset, byte[] out, int outOffset)
+ private 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);
+ byteBlockToInts(in, block, inOffset, 0);
for (int i = 0; i < ROUNDS; i++)
{
@@ -121,10 +122,10 @@ public class Shacal2Engine
ints2bytes(block, out, outOffset);
}
- public void decryptBlock(byte[] in, int inOffset, byte[] out, int outOffset)
- {
+ private void decryptBlock(byte[] in, int inOffset, byte[] out, int outOffset)
+ {
int[] block = new int[BLOCK_SIZE / 4];
- bytes2ints(in, block, inOffset, 0);
+ byteBlockToInts(in, block, inOffset, 0);
for (int i = ROUNDS - 1; i >-1; i--)
{
int tmp = block[0] - (((block[1] >>> 2) | (block[1] << -2))
@@ -177,6 +178,17 @@ public class Shacal2Engine
return BLOCK_SIZE;
}
+ private void byteBlockToInts(byte[] bytes, int[] block, int bytesPos, int blockPos)
+ {
+ for (int i = blockPos; i < BLOCK_SIZE / 4; i++)
+ {
+ block[i] = ((bytes[bytesPos++] & 0xFF) << 24)
+ | ((bytes[bytesPos++] & 0xFF) << 16)
+ | ((bytes[bytesPos++] & 0xFF) << 8)
+ | (bytes[bytesPos++] & 0xFF);
+ }
+ }
+
private void bytes2ints(byte[] bytes, int[] block, int bytesPos, int blockPos)
{
for (int i = blockPos; i < bytes.length / 4; i++)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
index 74bccfe7..33660224 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
@@ -9,13 +9,13 @@ import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
/**
* Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block
* sizes.
- * <p/>
+ * <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/>
+ * <p>
* Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
* Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
- * <p/>
+ * <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.
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 f16f6d44..364c5d82 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
@@ -91,7 +91,7 @@ public class VMPCEngine implements StreamCipher
n = 0;
}
- public void processBytes(byte[] in, int inOff, int len, byte[] out,
+ public int processBytes(byte[] in, int inOff, int len, byte[] out,
int outOff)
{
if ((inOff + len) > in.length)
@@ -117,6 +117,8 @@ public class VMPCEngine implements StreamCipher
// xor
out[i + outOff] = (byte) (in[i + inOff] ^ z);
}
+
+ return len;
}
public void reset()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java
index 5b2181da..45cc478e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java
@@ -1,6 +1,6 @@
package org.bouncycastle.crypto.engines;
-import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Pack;
/**
* Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce.
@@ -9,7 +9,6 @@ import org.bouncycastle.crypto.util.Pack;
*/
public class XSalsa20Engine extends Salsa20Engine
{
-
public String getAlgorithmName()
{
return "XSalsa20";
@@ -27,6 +26,11 @@ public class XSalsa20Engine extends Salsa20Engine
*/
protected void setKey(byte[] keyBytes, byte[] ivBytes)
{
+ if (keyBytes == null)
+ {
+ throw new IllegalArgumentException(getAlgorithmName() + " doesn't support re-init with null key");
+ }
+
if (keyBytes.length != 32)
{
throw new IllegalArgumentException(getAlgorithmName() + " requires a 256 bit key");
@@ -57,9 +61,5 @@ public class XSalsa20Engine extends Salsa20Engine
// 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/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html
new file mode 100644
index 00000000..e945dac0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Basic cipher classes.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/examples/DESExample.java b/bcprov/src/main/java/org/bouncycastle/crypto/examples/DESExample.java
index 16989971..90a21def 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/examples/DESExample.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/examples/DESExample.java
@@ -41,9 +41,11 @@ import org.bouncycastle.util.encoders.Hex;
* </ul>
* <p>
* When decrypting;
+ * <ul>
* <li>the infile is expected to be the 60 character wide base64
* encoded file
* <li>the keyfile is expected to be a base64 encoded file
+ * </ul>
* <p>
* This example shows how to use the light-weight API, DES and
* the filesystem for message encryption and decryption.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html
new file mode 100644
index 00000000..390a5400
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Simple examples of light weight API usage.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java
new file mode 100644
index 00000000..43d657da
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java
@@ -0,0 +1,660 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Core of password hashing scheme Bcrypt,
+ * designed by Niels Provos and David Mazières,
+ * corresponds to the C reference implementation.
+ * <p>
+ * This implementation does not correspondent to the 1999 published paper
+ * "A Future-Adaptable Password Scheme" of Niels Provos and David Mazières,
+ * see: https://www.usenix.org/legacy/events/usenix99/provos/provos_html/node1.html.
+ * In contrast to the paper, the order of key setup and salt setup is reversed:
+ * state <- ExpandKey(state, 0, key)
+ * state <- ExpandKey(state, 0, salt)
+ * This corresponds to the OpenBSD reference implementation of Bcrypt.
+ * </p><p>
+ * Note:
+ * There is no successful cryptanalysis (status 2015), but
+ * the amount of memory and the band width of Bcrypt
+ * may be insufficient to effectively prevent attacks
+ * with custom hardware like FPGAs, ASICs
+ * </p><p>
+ * This implementation uses some parts of Bouncy Castle's BlowfishEngine.
+ * </p>
+ */
+public final class BCrypt
+{
+ private static final BCrypt INSTANCE = new BCrypt();
+
+ // magic String "OrpheanBeholderScryDoubt" is used as clear text for encryption
+ private static final int[] MAGIC_STRING =
+ {
+ 0x4F727068, 0x65616E42, 0x65686F6C,
+ 0x64657253, 0x63727944, 0x6F756274
+ };
+ final static int MAGIC_STRING_LENGTH = 6;
+
+
+ private final static int[]
+ KP = {
+ 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
+ 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
+ 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
+ 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
+ 0x9216D5D9, 0x8979FB1B
+ },
+
+ KS0 = {
+ 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
+ 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
+ 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
+ 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
+ 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
+ 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
+ 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
+ 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
+ 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+ 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
+ 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
+ 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
+ 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
+ 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
+ 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
+ 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
+ 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
+ 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+ 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
+ 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
+ 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
+ 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
+ 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
+ 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
+ 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
+ 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
+ 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+ 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
+ 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
+ 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
+ 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
+ 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
+ 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
+ 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
+ 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
+ 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+ 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
+ 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
+ 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
+ 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
+ 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
+ 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
+ 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
+ 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
+ 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+ 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
+ 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
+ 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
+ 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
+ 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
+ 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
+ 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
+ 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
+ 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+ 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
+ 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
+ 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
+ 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
+ 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
+ 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
+ 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
+ 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
+ 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+ 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
+ },
+
+ KS1 = {
+ 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
+ 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
+ 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
+ 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
+ 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
+ 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
+ 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
+ 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
+ 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
+ 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
+ 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
+ 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
+ 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
+ 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
+ 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
+ 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
+ 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
+ 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
+ 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
+ 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
+ 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
+ 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
+ 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
+ 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
+ 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
+ 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
+ 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
+ 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
+ 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
+ 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
+ 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
+ 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
+ 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
+ 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
+ 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
+ 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
+ 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
+ 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
+ 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
+ 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
+ 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
+ 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
+ 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
+ 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
+ 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
+ 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
+ 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
+ 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
+ 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
+ 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
+ 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
+ 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
+ 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
+ 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
+ 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
+ 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
+ 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
+ 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
+ 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
+ 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
+ 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
+ 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
+ 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
+ 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
+ },
+
+ KS2 = {
+ 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
+ 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
+ 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
+ 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
+ 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
+ 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
+ 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
+ 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
+ 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
+ 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
+ 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
+ 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
+ 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
+ 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
+ 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
+ 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
+ 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
+ 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
+ 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
+ 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
+ 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
+ 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
+ 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
+ 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
+ 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
+ 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
+ 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
+ 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
+ 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
+ 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
+ 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
+ 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
+ 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
+ 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
+ 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
+ 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
+ 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
+ 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
+ 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
+ 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
+ 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
+ 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
+ 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
+ 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
+ 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
+ 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
+ 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
+ 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
+ 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
+ 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
+ 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
+ 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
+ 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
+ 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
+ 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
+ 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
+ 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
+ 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
+ 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
+ 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
+ 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
+ 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
+ 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
+ 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
+ },
+
+ KS3 = {
+ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
+ 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
+ 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
+ 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
+ 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
+ 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
+ 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
+ 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
+ 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+ 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
+ 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
+ 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
+ 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
+ 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
+ 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
+ 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
+ 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
+ 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+ 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
+ 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
+ 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
+ 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
+ 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
+ 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
+ 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
+ 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
+ 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+ 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
+ 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
+ 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
+ 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
+ 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
+ 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
+ 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
+ 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
+ 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+ 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
+ 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
+ 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
+ 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
+ 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
+ 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
+ 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
+ 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
+ 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+ 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
+ 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
+ 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
+ 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
+ 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
+ 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
+ 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
+ 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
+ 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+ 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
+ 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
+ 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
+ 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
+ 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
+ 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
+ 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
+ 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
+ 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+ 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
+ };
+
+ //====================================
+ // Useful constants
+ //====================================
+
+ private static final int ROUNDS = 16;
+ private static final int SBOX_SK = 256;
+ private static final int SBOX_SK2 = SBOX_SK * 2;
+ private static final int SBOX_SK3 = SBOX_SK * 3;
+ private static final int P_SZ = ROUNDS + 2;
+
+
+ private final int[] S; // the s-boxes
+ private final int[] P; // the p-array
+
+ public BCrypt()
+ {
+ S = new int[SBOX_SK * 4];
+ P = new int[P_SZ];
+ }
+
+ //==================================
+ // Private Implementation
+ //==================================
+
+ private int F(int x)
+ {
+ return (((S[(x >>> 24)] + S[SBOX_SK + ((x >>> 16) & 0xff)])
+ ^ S[SBOX_SK2 + ((x >>> 8) & 0xff)]) + S[SBOX_SK3 + (x & 0xff)]);
+ }
+
+ /*
+ * apply the encryption cycle to each value pair in the table.
+ */
+ private void processTable(
+ int xl,
+ int xr,
+ int[] table)
+ {
+ int size = table.length;
+
+ for (int s = 0; s < size; s += 2)
+ {
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ table[s] = xr;
+ table[s + 1] = xl;
+
+ xr = xl; // end of cycle swap
+ xl = table[s];
+ }
+ }
+
+ /*
+ * Initialize the S-boxes and the P-array, with a fixed string
+ * This string contains the hexadecimal digits of pi (3.141...)
+ */
+ private void initState()
+ {
+ System.arraycopy(KS0, 0, S, 0, SBOX_SK);
+ System.arraycopy(KS1, 0, S, SBOX_SK, SBOX_SK);
+ System.arraycopy(KS2, 0, S, SBOX_SK2, SBOX_SK);
+ System.arraycopy(KS3, 0, S, SBOX_SK3, SBOX_SK);
+
+ System.arraycopy(KP, 0, P, 0, P_SZ);
+
+ }
+
+ private int BytesTo32bits(byte[] b, int i)
+ {
+ return ((b[i] & 0xff) << 24) |
+ ((b[i + 1] & 0xff) << 16) |
+ ((b[i + 2] & 0xff) << 8) |
+ ((b[i + 3] & 0xff));
+ }
+
+ private void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset + 3] = (byte)in;
+ b[offset + 2] = (byte)(in >> 8);
+ b[offset + 1] = (byte)(in >> 16);
+ b[offset] = (byte)(in >> 24);
+ }
+
+ /*
+ * XOR P with key cyclic.
+ * This is the first part of ExpandKey function
+ */
+ private final void cyclicXorKey(byte[] key)
+ {
+ int keyLength = key.length;
+ int keyIndex = 0;
+
+ for (int i = 0; i < P_SZ; i++)
+ {
+ // get the 32 bits of the key, in 4 * 8 bit chunks
+ int data = 0x0000000;
+ for (int j = 0; j < 4; j++)
+ {
+ // create a 32 bit block
+ data = (data << 8) | (key[keyIndex++] & 0xff);
+
+ // wrap when we get to the end of the key
+ if (keyIndex >= keyLength)
+ {
+ keyIndex = 0;
+ }
+ }
+ // XOR the newly created 32 bit chunk onto the P-array
+ P[i] ^= data;
+ }
+ }
+
+
+ /*
+ * encrypt magic String 64 times in ECB
+ */
+ private byte[] encryptMagicString()
+ {
+ int[] text = {
+ MAGIC_STRING[0], MAGIC_STRING[1],
+ MAGIC_STRING[2], MAGIC_STRING[3],
+ MAGIC_STRING[4], MAGIC_STRING[5]
+ };
+ for (int i = 0; i < 64; i++)
+ {
+ for (int j = 0; j < MAGIC_STRING_LENGTH; j += 2)
+ {
+ int left = text[j];
+ int right = text[j + 1];
+
+ left ^= P[0];
+ for (int k = 1; k < ROUNDS; k += 2)
+ {
+ right ^= F(left) ^ P[k];
+ left ^= F(right) ^ P[k + 1];
+ }
+ right ^= P[ROUNDS + 1];
+ // swap values:
+ text[j] = right;
+ text[j + 1] = left;
+ }
+ }
+ byte[] result = new byte[24]; // holds 192 bit key
+ for (int i = 0; i < text.length; i++)
+ {
+ Bits32ToBytes(text[i], result, i * 4);
+ }
+ Arrays.fill(text, 0);
+ Arrays.fill(P, 0);
+ Arrays.fill(S, 0);
+
+ return result;
+ }
+
+ /*
+ * This is a part of Eksblowfish function
+ *
+ * @param table: sub-keys or working key
+ * @param salt32Bit: a 16 byte salt as two 32 bit words
+ * @param iv1: value from last proceeded table
+ * @param iv2: value from last proceeded table
+ */
+ private void processTableWithSalt(
+ int[] table,
+ int[] salt32Bit,
+ int iv1,
+ int iv2)
+ {
+ int xl = iv1 ^ salt32Bit[0];
+ int xr = iv2 ^ salt32Bit[1];
+
+ int yl;
+ int yr;
+ int size = table.length;
+
+ for (int s = 0; s < size; s += 4)
+ {
+ xl ^= P[0];
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+ xr ^= P[ROUNDS + 1];
+
+ table[s] = xr;
+ table[s + 1] = xl;
+
+ yl = salt32Bit[2] ^ xr;
+ yr = salt32Bit[3] ^ xl;
+
+ if (s + 2 >= size) // P holds 18 values
+ {
+ break;
+ }
+
+ yl ^= P[0];
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ yr ^= F(yl) ^ P[i];
+ yl ^= F(yr) ^ P[i + 1];
+ }
+ yr ^= P[ROUNDS + 1];
+
+ table[s + 2] = yr;
+ table[s + 3] = yl;
+
+ xl = salt32Bit[0] ^ yr;
+ xr = salt32Bit[1] ^ yl;
+ }
+ }
+
+ /**
+ * Derives a raw 192 bit Bcrypt key
+ *
+ * @param cost the cost factor, treated as an exponent of 2
+ * @param salt a 16 byte salt
+ * @param psw the password
+ * @return a 192 bit key
+ */
+ private final byte[] deriveRawKey(
+ int cost,
+ byte[] salt,
+ byte[] psw)
+ {
+ if (salt.length != 16)
+ {
+ throw new DataLengthException("Invalid salt size: 16 bytes expected.");
+ }
+ if (cost < 4 || cost > 31)
+ {
+ throw new IllegalArgumentException("Illegal cost factor: 4 - 31 expected.");
+ }
+
+ if (psw.length == 0)
+ {
+ psw = new byte[4];
+ }
+
+ // state <- InitState()
+ initState();
+
+ int[] salt32Bit = new int[4]; // holds 16 byte salt
+ salt32Bit[0] = BytesTo32bits(salt, 0);
+ salt32Bit[1] = BytesTo32bits(salt, 4);
+ salt32Bit[2] = BytesTo32bits(salt, 8);
+ salt32Bit[3] = BytesTo32bits(salt, 12);
+
+ int[] salt32Bit2 = new int[salt.length]; // swapped values
+ salt32Bit2[0] = salt32Bit[2];
+ salt32Bit2[1] = salt32Bit[3];
+ salt32Bit2[2] = salt32Bit[0];
+ salt32Bit2[3] = salt32Bit[1];
+
+ // ExpandKey( state, salt, key):
+ cyclicXorKey(psw);
+ processTableWithSalt(P, salt32Bit, 0, 0);
+ Arrays.fill(salt32Bit, 0);
+ processTableWithSalt(S, salt32Bit2, P[P.length - 2], P[P.length - 1]);
+ Arrays.fill(salt32Bit2, 0);
+
+ int rounds = 1 << cost;
+ for (int i = 0; i != rounds; i++) // rounds may be negative if cost is 31
+ {
+ // state <- ExpandKey(state, 0, key);
+ cyclicXorKey(psw);
+ processTable(0, 0, P);
+ processTable(P[P_SZ - 2], P[P_SZ - 1], S);
+
+ // state <- ExpandKey(state, 0, salt);
+ cyclicXorKey(salt);
+ processTable(0, 0, P);
+ processTable(P[P_SZ - 2], P[P_SZ - 1], S);
+ }
+
+ // encrypt magicString 64 times
+ return encryptMagicString();
+ }
+
+ /**
+ * Size of the salt parameter in bytes
+ */
+ static final int SALT_SIZE_BYTES = 16;
+
+ /**
+ * Minimum value of cost parameter, equal to log2(bytes of salt)
+ */
+ static final int MIN_COST = 4;
+
+ /**
+ * Maximum value of cost parameter (31 == 2,147,483,648)
+ */
+ static final int MAX_COST = 31;
+
+ /**
+ * Maximum size of password == max (unrestricted) size of Blowfish key
+ */
+ // Blowfish spec limits keys to 448bit/56 bytes to ensure all bits of key affect all ciphertext
+ // bits, but technically algorithm handles 72 byte keys and most implementations support this.
+ static final int MAX_PASSWORD_BYTES = 72;
+
+ /**
+ * Calculates the <b>bcrypt</b> hash of a password.
+ * <p>
+ * This implements the raw <b>bcrypt</b> function as defined in the bcrypt specification, not
+ * the crypt encoded version implemented in OpenBSD.
+ * </p>
+ * @param password the password bytes (up to 72 bytes) to use for this invocation.
+ * @param salt the 128 bit salt to use for this invocation.
+ * @param cost the bcrypt cost parameter. The cost of the bcrypt function grows as
+ * <code>2^cost</code>. Legal values are 4..31 inclusive.
+ * @return the output of the raw bcrypt operation: a 192 bit (24 byte) hash.
+ */
+ public static byte[] generate(byte[] password, byte[] salt, int cost)
+ {
+ if (password == null || salt == null)
+ {
+ throw new IllegalArgumentException("Password and salt are required");
+ }
+ if (salt.length != SALT_SIZE_BYTES)
+ {
+ throw new IllegalArgumentException("BCrypt salt must be 128 bits");
+ }
+ if (password.length > MAX_PASSWORD_BYTES)
+ {
+ throw new IllegalArgumentException("BCrypt password must be <= 72 bytes");
+ }
+ if (cost < MIN_COST || cost > MAX_COST)
+ {
+ throw new IllegalArgumentException("BCrypt cost must be from 4..31");
+ }
+
+ return INSTANCE.deriveRawKey(cost, salt, password);
+ }
+} \ No newline at end of file
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 16c8b915..b284a931 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
@@ -6,7 +6,7 @@ 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;
+import org.bouncycastle.util.Pack;
/**
* Basic KDF generator for derived keys and ivs as defined by IEEE P1363a/ISO
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupKeyPairGenerator.java
new file mode 100644
index 00000000..8fcdf858
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupKeyPairGenerator.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.params.CramerShoupKeyGenerationParameters;
+import org.bouncycastle.crypto.params.CramerShoupParameters;
+import org.bouncycastle.crypto.params.CramerShoupPrivateKeyParameters;
+import org.bouncycastle.crypto.params.CramerShoupPublicKeyParameters;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * a Cramer Shoup key pair generator
+ *
+ */
+public class CramerShoupKeyPairGenerator implements AsymmetricCipherKeyPairGenerator {
+
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private CramerShoupKeyGenerationParameters param;
+
+ public void init(KeyGenerationParameters param) {
+ this.param = (CramerShoupKeyGenerationParameters) param;
+ }
+
+ public AsymmetricCipherKeyPair generateKeyPair() {
+ CramerShoupParameters csParams = param.getParameters();
+
+ CramerShoupPrivateKeyParameters sk = generatePrivateKey(param.getRandom(), csParams);
+ CramerShoupPublicKeyParameters pk = calculatePublicKey(csParams, sk);
+ sk.setPk(pk);
+
+ return new AsymmetricCipherKeyPair(pk, sk);
+ }
+
+ private BigInteger generateRandomElement(BigInteger p, SecureRandom random) {
+ return BigIntegers.createRandomInRange(ONE, p.subtract(ONE), random);
+ }
+
+ private CramerShoupPrivateKeyParameters generatePrivateKey(SecureRandom random, CramerShoupParameters csParams){
+ BigInteger p = csParams.getP();
+ CramerShoupPrivateKeyParameters key = new CramerShoupPrivateKeyParameters(csParams,
+ generateRandomElement(p, random), generateRandomElement(p, random),
+ generateRandomElement(p, random), generateRandomElement(p, random),
+ generateRandomElement(p, random));
+ return key;
+ }
+
+ private CramerShoupPublicKeyParameters calculatePublicKey(CramerShoupParameters csParams, CramerShoupPrivateKeyParameters sk) {
+ BigInteger g1 = csParams.getG1();
+ BigInteger g2 = csParams.getG2();
+ BigInteger p = csParams.getP();
+
+ BigInteger c = g1.modPow(sk.getX1(), p).multiply(g2.modPow(sk.getX2(), p));
+ BigInteger d = g1.modPow(sk.getY1(), p).multiply(g2.modPow(sk.getY2(), p));
+ BigInteger h = g1.modPow(sk.getZ(), p);
+
+ return new CramerShoupPublicKeyParameters(csParams, c, d, h);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupParametersGenerator.java
new file mode 100644
index 00000000..22dd34af
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/CramerShoupParametersGenerator.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.params.CramerShoupParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.util.BigIntegers;
+
+public class CramerShoupParametersGenerator
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private int size;
+ private int certainty;
+ private SecureRandom random;
+
+ /**
+ * Initialise the parameters generator.
+ *
+ * @param size bit length for the prime p
+ * @param certainty a measure of the uncertainty that the caller is willing to tolerate:
+ * the probability that the generated modulus is prime exceeds (1 - 1/2^certainty).
+ * The execution time of this method is proportional to the value of this parameter.
+ * @param random a source of randomness
+ */
+ public void init(int size, int certainty, SecureRandom random)
+ {
+ this.size = size;
+ this.certainty = certainty;
+ this.random = random;
+ }
+
+ /**
+ * which generates the p and g values from the given parameters, returning
+ * the CramerShoupParameters object.
+ * <p>
+ * Note: can take a while...
+ * </p>
+ */
+ public CramerShoupParameters generateParameters()
+ {
+ //
+ // find a safe prime p where p = 2*q + 1, where p and q are prime.
+ //
+ BigInteger[] safePrimes = ParametersHelper.generateSafePrimes(size, certainty, random);
+
+// BigInteger p = safePrimes[0];
+ BigInteger q = safePrimes[1];
+ BigInteger g1 = ParametersHelper.selectGenerator(q, random);
+ BigInteger g2 = ParametersHelper.selectGenerator(q, random);
+ while (g1.equals(g2))
+ {
+ g2 = ParametersHelper.selectGenerator(q, random);
+ }
+
+ return new CramerShoupParameters(q, g1, g2, new SHA256Digest());
+ }
+
+ public CramerShoupParameters generateParameters(DHParameters dhParams)
+ {
+ BigInteger p = dhParams.getP();
+ BigInteger g1 = dhParams.getG();
+
+ // now we just need a second generator
+ BigInteger g2 = ParametersHelper.selectGenerator(p, random);
+ while (g1.equals(g2))
+ {
+ g2 = ParametersHelper.selectGenerator(p, random);
+ }
+
+ return new CramerShoupParameters(p, g1, g2, new SHA256Digest());
+ }
+
+ private static class ParametersHelper
+ {
+
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ /*
+ * Finds a pair of prime BigInteger's {p, q: p = 2q + 1}
+ *
+ * (see: Handbook of Applied Cryptography 4.86)
+ */
+ static BigInteger[] generateSafePrimes(int size, int certainty, SecureRandom random)
+ {
+ BigInteger p, q;
+ int qLength = size - 1;
+
+ for (; ; )
+ {
+ q = new BigInteger(qLength, 2, random);
+ p = q.shiftLeft(1).add(ONE);
+ if (p.isProbablePrime(certainty) && (certainty <= 2 || q.isProbablePrime(certainty)))
+ {
+ break;
+ }
+ }
+
+ return new BigInteger[]{p, q};
+ }
+
+ static BigInteger selectGenerator(BigInteger p, SecureRandom random)
+ {
+ BigInteger pMinusTwo = p.subtract(TWO);
+ BigInteger g;
+
+ /*
+ * RFC 2631 2.2.1.2 (and see: Handbook of Applied Cryptography 4.81)
+ */
+ do
+ {
+ BigInteger h = BigIntegers.createRandomInRange(TWO, pMinusTwo, random);
+
+ g = h.modPow(TWO, p);
+ }
+ while (g.equals(ONE));
+
+ return g;
+ }
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
index e0d86fc3..6795ec96 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper.java
@@ -4,6 +4,7 @@ import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.math.ec.WNafUtil;
import org.bouncycastle.util.BigIntegers;
class DHKeyGeneratorHelper
@@ -19,12 +20,19 @@ class DHKeyGeneratorHelper
BigInteger calculatePrivate(DHParameters dhParams, SecureRandom random)
{
- BigInteger p = dhParams.getP();
int limit = dhParams.getL();
if (limit != 0)
{
- return new BigInteger(limit, random).setBit(limit - 1);
+ int minWeight = limit >>> 2;
+ for (;;)
+ {
+ BigInteger x = new BigInteger(limit, random).setBit(limit - 1);
+ if (WNafUtil.getNafWeight(x) >= minWeight)
+ {
+ return x;
+ }
+ }
}
BigInteger min = TWO;
@@ -34,14 +42,22 @@ class DHKeyGeneratorHelper
min = ONE.shiftLeft(m - 1);
}
- BigInteger max = p.subtract(TWO);
BigInteger q = dhParams.getQ();
- if (q != null)
+ if (q == null)
{
- max = q.subtract(TWO);
+ q = dhParams.getP();
}
+ BigInteger max = q.subtract(TWO);
- return BigIntegers.createRandomInRange(min, max, random);
+ int minWeight = max.bitLength() >>> 2;
+ for (;;)
+ {
+ BigInteger x = BigIntegers.createRandomInRange(min, max, random);
+ if (WNafUtil.getNafWeight(x) >= minWeight)
+ {
+ return x;
+ }
+ }
}
BigInteger calculatePublic(DHParameters dhParams, BigInteger x)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
index 118bc9cc..dc0a5ae1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DHParametersHelper.java
@@ -3,6 +3,7 @@ package org.bouncycastle.crypto.generators;
import java.math.BigInteger;
import java.security.SecureRandom;
+import org.bouncycastle.math.ec.WNafUtil;
import org.bouncycastle.util.BigIntegers;
class DHParametersHelper
@@ -19,6 +20,7 @@ class DHParametersHelper
{
BigInteger p, q;
int qLength = size - 1;
+ int minWeight = size >>> 2;
for (;;)
{
@@ -27,10 +29,28 @@ class DHParametersHelper
// p <- 2q + 1
p = q.shiftLeft(1).add(ONE);
- if (p.isProbablePrime(certainty) && (certainty <= 2 || q.isProbablePrime(certainty)))
+ if (!p.isProbablePrime(certainty))
{
- break;
+ continue;
}
+
+ if (certainty > 2 && !q.isProbablePrime(certainty - 2))
+ {
+ continue;
+ }
+
+ /*
+ * Require a minimum weight of the NAF representation, since low-weight primes may be
+ * weak against a version of the number-field-sieve for the discrete-logarithm-problem.
+ *
+ * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+ */
+ if (WNafUtil.getNafWeight(p) < minWeight)
+ {
+ continue;
+ }
+
+ break;
}
return new BigInteger[] { p, q };
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
index 93f49cfa..ff3df358 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAKeyPairGenerator.java
@@ -1,5 +1,8 @@
package org.bouncycastle.crypto.generators;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.KeyGenerationParameters;
@@ -7,11 +10,9 @@ import org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.math.ec.WNafUtil;
import org.bouncycastle.util.BigIntegers;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
/**
* a DSA key pair generator.
*
@@ -45,13 +46,20 @@ public class DSAKeyPairGenerator
private static BigInteger generatePrivateKey(BigInteger q, SecureRandom random)
{
- // TODO Prefer this method? (change test cases that used fixed random)
- // B.1.1 Key Pair Generation Using Extra Random Bits
-// BigInteger c = new BigInteger(q.bitLength() + 64, random);
-// return c.mod(q.subtract(ONE)).add(ONE);
-
// B.1.2 Key Pair Generation by Testing Candidates
- return BigIntegers.createRandomInRange(ONE, q.subtract(ONE), random);
+ int minWeight = q.bitLength() >>> 2;
+ for (;;)
+ {
+ // TODO Prefer this method? (change test cases that used fixed random)
+ // B.1.1 Key Pair Generation Using Extra Random Bits
+// BigInteger x = new BigInteger(q.bitLength() + 64, random).mod(q.subtract(ONE)).add(ONE);
+
+ BigInteger x = BigIntegers.createRandomInRange(ONE, q.subtract(ONE), random);
+ if (WNafUtil.getNafWeight(x) >= minWeight)
+ {
+ return x;
+ }
+ }
}
private static BigInteger calculatePublicKey(BigInteger p, BigInteger g, BigInteger x)
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 f7a3df23..fe98b7c8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
@@ -42,7 +42,7 @@ public class DSAParametersGenerator
/**
* initialise the key generator.
*
- * @param size size of the key (range 2^512 -> 2^1024 - 64 bit increments)
+ * @param size size of the key (range 2^512 -&gt; 2^1024 - 64 bit increments)
* @param certainty measure of robustness of prime (for FIPS 186-2 compliance this should be at least 80).
* @param random random byte source.
*/
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 d5f5fc85..4f46a38d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
@@ -11,7 +11,10 @@ import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
+import org.bouncycastle.math.ec.WNafUtil;
public class ECKeyPairGenerator
implements AsymmetricCipherKeyPairGenerator, ECConstants
@@ -40,19 +43,42 @@ public class ECKeyPairGenerator
public AsymmetricCipherKeyPair generateKeyPair()
{
BigInteger n = params.getN();
- int nBitLength = n.bitLength();
- BigInteger d;
+ int nBitLength = n.bitLength();
+ int minWeight = nBitLength >>> 2;
- do
+ BigInteger d;
+ for (;;)
{
d = new BigInteger(nBitLength, random);
+
+ if (d.compareTo(TWO) < 0 || (d.compareTo(n) >= 0))
+ {
+ continue;
+ }
+
+ /*
+ * Require a minimum weight of the NAF representation, since low-weight primes may be
+ * weak against a version of the number-field-sieve for the discrete-logarithm-problem.
+ *
+ * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+ */
+ if (WNafUtil.getNafWeight(d) < minWeight)
+ {
+ continue;
+ }
+
+ break;
}
- while (d.equals(ZERO) || (d.compareTo(n) >= 0));
- ECPoint Q = params.getG().multiply(d);
+ ECPoint Q = createBasePointMultiplier().multiply(params.getG(), d);
return new AsymmetricCipherKeyPair(
new ECPublicKeyParameters(Q, params),
new ECPrivateKeyParameters(d, params));
}
+
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java
index 3e13c212..8948f931 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/GOST3410KeyPairGenerator.java
@@ -7,6 +7,7 @@ import org.bouncycastle.crypto.params.GOST3410KeyGenerationParameters;
import org.bouncycastle.crypto.params.GOST3410Parameters;
import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
+import org.bouncycastle.math.ec.WNafUtil;
import java.math.BigInteger;
import java.security.SecureRandom;
@@ -19,8 +20,6 @@ import java.security.SecureRandom;
public class GOST3410KeyPairGenerator
implements AsymmetricCipherKeyPairGenerator
{
- private static final BigInteger ZERO = BigInteger.valueOf(0);
-
private GOST3410KeyGenerationParameters param;
public void init(
@@ -39,11 +38,29 @@ public class GOST3410KeyPairGenerator
p = GOST3410Params.getP();
a = GOST3410Params.getA();
- do
+ int minWeight = 64;
+ for (;;)
{
x = new BigInteger(256, random);
+
+ if (x.signum() < 1 || x.compareTo(q) >= 0)
+ {
+ continue;
+ }
+
+ /*
+ * Require a minimum weight of the NAF representation, since low-weight primes may be
+ * weak against a version of the number-field-sieve for the discrete-logarithm-problem.
+ *
+ * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+ */
+ if (WNafUtil.getNafWeight(x) < minWeight)
+ {
+ continue;
+ }
+
+ break;
}
- while (x.equals(ZERO) || x.compareTo(q) >= 0);
//
// calculate the public key.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java
index 8e93e6b0..75915cf2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/HKDFBytesGenerator.java
@@ -11,7 +11,7 @@ import org.bouncycastle.crypto.params.KeyParameter;
/**
* HMAC-based Extract-and-Expand Key Derivation Function (HKDF) implemented
* according to IETF RFC 5869, May 2010 as specified by H. Krawczyk, IBM
- * Research & P. Eronen, Nokia. It uses a HMac internally to compute de OKM
+ * Research &amp; P. Eronen, Nokia. It uses a HMac internally to compute de OKM
* (output keying material) and is likely to have better security properties
* than KDF's based on just a hash function.
*/
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
index 306530ec..7147add7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
@@ -11,6 +11,31 @@ import org.bouncycastle.crypto.params.KeyParameter;
/**
* This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ * NIST SP800-108 allows for alternative orderings of the input fields, meaning that the input can be formated in multiple ways.
+ * There are 3 supported formats: - Below [i]_2 is a counter of r-bits length concatenated to the fixedInputData.
+ * <ul>
+ * <li>1: K(i) := PRF( KI, [i]_2 || Label || 0x00 || Context || [L]_2 ) with the counter at the very beginning of the fixedInputData (The default implementation has this format)</li>
+ * <li>2: K(i) := PRF( KI, Label || 0x00 || Context || [L]_2 || [i]_2 ) with the counter at the very end of the fixedInputData</li>
+ * <li>3a: K(i) := PRF( KI, Label || 0x00 || [i]_2 || Context || [L]_2 ) OR:</li>
+ * <li>3b: K(i) := PRF( KI, Label || 0x00 || [i]_2 || [L]_2 || Context ) OR:</li>
+ * <li>3c: K(i) := PRF( KI, Label || [i]_2 || 0x00 || Context || [L]_2 ) etc... with the counter somewhere in the 'middle' of the fixedInputData.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This function must be called with the following KDFCounterParameters():
+ * - KI <br/>
+ * - The part of the fixedInputData that comes BEFORE the counter OR null <br/>
+ * - the part of the fixedInputData that comes AFTER the counter OR null <br/>
+ * - the length of the counter in bits (not bytes)
+ * </p>
+ * Resulting function calls assuming an 8 bit counter.
+ * <ul>
+ * <li>1. KDFCounterParameters(ki, null, "Label || 0x00 || Context || [L]_2]", 8);</li>
+ * <li>2. KDFCounterParameters(ki, "Label || 0x00 || Context || [L]_2]", null, 8);</li>
+ * <li>3a. KDFCounterParameters(ki, "Label || 0x00", "Context || [L]_2]", 8);</li>
+ * <li>3b. KDFCounterParameters(ki, "Label || 0x00", "[L]_2] || Context", 8);</li>
+ * <li>3c. KDFCounterParameters(ki, "Label", "0x00 || Context || [L]_2]", 8);</li>
+ * </ul>
*/
public class KDFCounterBytesGenerator
implements MacDerivationFunction
@@ -27,7 +52,8 @@ public class KDFCounterBytesGenerator
private final int h;
// fields set by init
- private byte[] fixedInputData;
+ private byte[] fixedInputDataCtrPrefix;
+ private byte[] fixedInputData_afterCtr;
private int maxSizeExcl;
// ios is i defined as an octet string (the binary representation)
private byte[] ios;
@@ -61,7 +87,8 @@ public class KDFCounterBytesGenerator
// --- set arguments ---
- this.fixedInputData = kdfParams.getFixedInputData();
+ this.fixedInputDataCtrPrefix = kdfParams.getFixedInputDataCounterPrefix();
+ this.fixedInputData_afterCtr = kdfParams.getFixedInputDataCounterSuffix();
int r = kdfParams.getR();
this.ios = new byte[r / 8];
@@ -145,8 +172,9 @@ public class KDFCounterBytesGenerator
// special case for K(0): K(0) is empty, so no update
+ prf.update(fixedInputDataCtrPrefix, 0, fixedInputDataCtrPrefix.length);
prf.update(ios, 0, ios.length);
- prf.update(fixedInputData, 0, fixedInputData.length);
+ prf.update(fixedInputData_afterCtr, 0, fixedInputData_afterCtr.length);
prf.doFinal(k, 0);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenBSDBCrypt.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenBSDBCrypt.java
new file mode 100644
index 00000000..b5f133fe
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/OpenBSDBCrypt.java
@@ -0,0 +1,315 @@
+package org.bouncycastle.crypto.generators;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Password hashing scheme BCrypt,
+ * designed by Niels Provos and David Mazières, using the
+ * String format and the Base64 encoding
+ * of the reference implementation on OpenBSD
+ */
+public class OpenBSDBCrypt
+{
+ private static final byte[] encodingTable = // the Bcrypts encoding table for OpenBSD
+ {
+ (byte)'.', (byte)'/', (byte)'A', (byte)'B', (byte)'C', (byte)'D',
+ (byte)'E', (byte)'F', (byte)'G', (byte)'H', (byte)'I', (byte)'J',
+ (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P',
+ (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V',
+ (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b',
+ (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h',
+ (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+ (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t',
+ (byte)'u', (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
+ (byte)'6', (byte)'7', (byte)'8', (byte)'9'
+ };
+ /*
+ * set up the decoding table.
+ */
+ private static final byte[] decodingTable = new byte[128];
+ private static final String version = "2a"; // previous version was not UTF-8
+
+ static
+ {
+ for (int i = 0; i < decodingTable.length; i++)
+ {
+ decodingTable[i] = (byte)0xff;
+ }
+
+ for (int i = 0; i < encodingTable.length; i++)
+ {
+ decodingTable[encodingTable[i]] = (byte)i;
+ }
+ }
+
+ public OpenBSDBCrypt()
+ {
+
+ }
+
+ /**
+ * Creates a 60 character Bcrypt String, including
+ * version, cost factor, salt and hash, separated by '$'
+ *
+ * @param cost the cost factor, treated as an exponent of 2
+ * @param salt a 16 byte salt
+ * @param password the password
+ * @return a 60 character Bcrypt String
+ */
+ private static String createBcryptString(
+ byte[] password,
+ byte[] salt,
+ int cost)
+ {
+ StringBuffer sb = new StringBuffer(60);
+ sb.append('$');
+ sb.append(version);
+ sb.append('$');
+ sb.append(cost < 10 ? ("0" + cost) : Integer.toString(cost));
+ sb.append('$');
+ sb.append(encodeData(salt));
+
+ byte[] key = BCrypt.generate(password, salt, cost);
+
+ sb.append(encodeData(key));
+
+ return sb.toString();
+ }
+
+ /**
+ * Creates a 60 character Bcrypt String, including
+ * version, cost factor, salt and hash, separated by '$'
+ *
+ * @param cost the cost factor, treated as an exponent of 2
+ * @param salt a 16 byte salt
+ * @param password the password
+ * @return a 60 character Bcrypt String
+ */
+ public static String generate(
+ char[] password,
+ byte[] salt,
+ int cost)
+ {
+ if (password == null)
+ {
+ throw new IllegalArgumentException("Password required.");
+ }
+ if (salt == null)
+ {
+ throw new IllegalArgumentException("Salt required.");
+ }
+ else if (salt.length != 16)
+ {
+ throw new DataLengthException("16 byte salt required: " + salt.length);
+ }
+ if (cost < 4 || cost > 31) // Minimum rounds: 16, maximum 2^31
+ {
+ throw new IllegalArgumentException("Invalid cost factor.");
+ }
+
+ byte[] psw = Strings.toUTF8ByteArray(password);
+
+ // 0 termination:
+
+ byte[] tmp = new byte[psw.length >= 72 ? 72 : psw.length + 1];
+
+ if (tmp.length > psw.length)
+ {
+ System.arraycopy(psw, 0, tmp, 0, psw.length);
+ }
+ else
+ {
+ System.arraycopy(psw, 0, tmp, 0, tmp.length);
+ }
+
+ Arrays.fill(psw, (byte)0);
+
+ String rv = createBcryptString(tmp, salt, cost);
+
+ Arrays.fill(tmp, (byte)0);
+
+ return rv;
+ }
+
+ /**
+ * Checks if a password corresponds to a 60 character Bcrypt String
+ *
+ * @param bcryptString a 60 character Bcrypt String, including
+ * version, cost factor, salt and hash,
+ * separated by '$'
+ * @param password the password as an array of chars
+ * @return true if the password corresponds to the
+ * Bcrypt String, otherwise false
+ */
+ public static boolean checkPassword(
+ String bcryptString,
+ char[] password)
+ {
+ // validate bcryptString:
+ if (bcryptString.length() != 60)
+ {
+ throw new DataLengthException("Bcrypt String length: "
+ + bcryptString.length() + ", 60 required.");
+ }
+ if (bcryptString.charAt(0) != '$'
+ || bcryptString.charAt(3) != '$'
+ || bcryptString.charAt(6) != '$')
+ {
+ throw new IllegalArgumentException("Invalid Bcrypt String format.");
+ }
+ if (!bcryptString.substring(1, 3).equals(version))
+ {
+ throw new IllegalArgumentException("Wrong Bcrypt version, 2a expected.");
+ }
+ int cost = 0;
+ try
+ {
+ cost = Integer.parseInt(bcryptString.substring(4, 6));
+ }
+ catch (NumberFormatException nfe)
+ {
+ throw new IllegalArgumentException("Invalid cost factor:"
+ + bcryptString.substring(4, 6));
+ }
+ if (cost < 4 || cost > 31)
+ {
+ throw new IllegalArgumentException("Invalid cost factor: "
+ + cost + ", 4 < cost < 31 expected.");
+ }
+ // check password:
+ if (password == null)
+ {
+ throw new IllegalArgumentException("Missing password.");
+ }
+ byte[] salt = decodeSaltString(
+ bcryptString.substring(bcryptString.lastIndexOf('$') + 1,
+ bcryptString.length() - 31));
+
+ String newBcryptString = generate(password, salt, cost);
+
+ return bcryptString.equals(newBcryptString);
+ }
+
+ /*
+ * encode the input data producing a Bcrypt base 64 String.
+ *
+ * @param a byte representation of the salt or the password
+ * @return the Bcrypt base64 String
+ */
+ private static String encodeData(
+ byte[] data)
+
+ {
+ if (data.length != 24 && data.length != 16) // 192 bit key or 128 bit salt expected
+ {
+ throw new DataLengthException("Invalid length: " + data.length + ", 24 for key or 16 for salt expected");
+ }
+ boolean salt = false;
+ if (data.length == 16)//salt
+ {
+ salt = true;
+ byte[] tmp = new byte[18];// zero padding
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ data = tmp;
+ }
+ else // key
+ {
+ data[data.length - 1] = (byte)0;
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int len = data.length;
+
+ int a1, a2, a3;
+ int i;
+ for (i = 0; i < len; i += 3)
+ {
+ a1 = data[i] & 0xff;
+ a2 = data[i + 1] & 0xff;
+ a3 = data[i + 2] & 0xff;
+
+ out.write(encodingTable[(a1 >>> 2) & 0x3f]);
+ out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
+ out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
+ out.write(encodingTable[a3 & 0x3f]);
+ }
+
+ String result = Strings.fromByteArray(out.toByteArray());
+ if (salt == true)// truncate padding
+ {
+ return result.substring(0, 22);
+ }
+ else
+ {
+ return result.substring(0, result.length() - 1);
+ }
+ }
+
+
+ /*
+ * decodes the bcrypt base 64 encoded SaltString
+ *
+ * @param a 22 character Bcrypt base 64 encoded String
+ * @return the 16 byte salt
+ * @exception DataLengthException if the length
+ * of parameter is not 22
+ * @exception InvalidArgumentException if the parameter
+ * contains a value other than from Bcrypts base 64 encoding table
+ */
+ private static byte[] decodeSaltString(
+ String saltString)
+ {
+ char[] saltChars = saltString.toCharArray();
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream(16);
+ byte b1, b2, b3, b4;
+
+ if (saltChars.length != 22)// bcrypt salt must be 22 (16 bytes)
+ {
+ throw new DataLengthException("Invalid base64 salt length: " + saltChars.length + " , 22 required.");
+ }
+
+ // check String for invalid characters:
+ for (int i = 0; i < saltChars.length; i++)
+ {
+ int value = saltChars[i];
+ if (value > 122 || value < 46 || (value > 57 && value < 65))
+ {
+ throw new IllegalArgumentException("Salt string contains invalid character: " + value);
+ }
+ }
+
+ // Padding: add two '\u0000'
+ char[] tmp = new char[22 + 2];
+ System.arraycopy(saltChars, 0, tmp, 0, saltChars.length);
+ saltChars = tmp;
+
+ int len = saltChars.length;
+
+ for (int i = 0; i < len; i += 4)
+ {
+ b1 = decodingTable[saltChars[i]];
+ b2 = decodingTable[saltChars[i + 1]];
+ b3 = decodingTable[saltChars[i + 2]];
+ b4 = decodingTable[saltChars[i + 3]];
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+ out.write((b3 << 6) | b4);
+ }
+
+ byte[] saltBytes = out.toByteArray();
+
+ // truncate:
+ byte[] tmpSalt = new byte[16];
+ System.arraycopy(saltBytes, 0, tmpSalt, 0, tmpSalt.length);
+ saltBytes = tmpSalt;
+
+ return saltBytes;
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java
index 91659730..59eee39e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java
@@ -53,7 +53,7 @@ public class Poly1305KeyGenerator
* <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>
+ * @param key a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code>
*/
public static void clamp(byte[] key)
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
index f58069d5..7277045e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/RSAKeyPairGenerator.java
@@ -1,13 +1,14 @@
package org.bouncycastle.crypto.generators;
+import java.math.BigInteger;
+
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
-
-import java.math.BigInteger;
+import org.bouncycastle.math.ec.WNafUtil;
/**
* an RSA key pair generator.
@@ -19,129 +20,156 @@ public class RSAKeyPairGenerator
private RSAKeyGenerationParameters param;
- public void init(
- KeyGenerationParameters param)
+ public void init(KeyGenerationParameters param)
{
this.param = (RSAKeyGenerationParameters)param;
}
public AsymmetricCipherKeyPair generateKeyPair()
{
- BigInteger p, q, n, d, e, pSub1, qSub1, phi;
+ AsymmetricCipherKeyPair result = null;
+ boolean done = false;
- //
- // p and q values should have a length of half the strength in bits
- //
- int strength = param.getStrength();
- int pbitlength = (strength + 1) / 2;
- int qbitlength = strength - pbitlength;
- int mindiffbits = strength / 3;
+ while (!done)
+ {
+ BigInteger p, q, n, d, e, pSub1, qSub1, phi, lcm, dLowerBound;
- e = param.getPublicExponent();
+ //
+ // p and q values should have a length of half the strength in bits
+ //
+ int strength = param.getStrength();
+ int pbitlength = (strength + 1) / 2;
+ int qbitlength = strength - pbitlength;
+ int mindiffbits = strength / 3;
+ int minWeight = strength >> 2;
- // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes)
- // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")
+ e = param.getPublicExponent();
- //
- // generate p, prime and (p-1) relatively prime to e
- //
- for (;;)
- {
- p = new BigInteger(pbitlength, 1, param.getRandom());
-
- if (p.mod(e).equals(ONE))
- {
- continue;
- }
-
- if (!p.isProbablePrime(param.getCertainty()))
- {
- continue;
- }
-
- if (e.gcd(p.subtract(ONE)).equals(ONE))
- {
- break;
- }
- }
+ // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes)
+ // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")
- //
- // generate a modulus of the required length
- //
- for (;;)
- {
- // generate q, prime and (q-1) relatively prime to e,
- // and not equal to p
+ p = chooseRandomPrime(pbitlength, e);
+
+ //
+ // generate a modulus of the required length
//
for (;;)
{
- q = new BigInteger(qbitlength, 1, param.getRandom());
+ q = chooseRandomPrime(qbitlength, e);
- if (q.subtract(p).abs().bitLength() < mindiffbits)
+ // p and q should not be too close together (or equal!)
+ BigInteger diff = q.subtract(p).abs();
+ if (diff.bitLength() < mindiffbits)
{
continue;
}
-
- if (q.mod(e).equals(ONE))
+
+ //
+ // calculate the modulus
+ //
+ n = p.multiply(q);
+
+ if (n.bitLength() != strength)
{
+ //
+ // if we get here our primes aren't big enough, make the largest
+ // of the two p and try again
+ //
+ p = p.max(q);
continue;
}
-
- if (!q.isProbablePrime(param.getCertainty()))
+
+ /*
+ * Require a minimum weight of the NAF representation, since low-weight composites may
+ * be weak against a version of the number-field-sieve for factoring.
+ *
+ * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+ */
+ if (WNafUtil.getNafWeight(n) < minWeight)
{
+ p = chooseRandomPrime(pbitlength, e);
continue;
}
-
- if (e.gcd(q.subtract(ONE)).equals(ONE))
- {
- break;
- }
+
+ break;
+ }
+
+ if (p.compareTo(q) < 0)
+ {
+ phi = p;
+ p = q;
+ q = phi;
}
+ pSub1 = p.subtract(ONE);
+ qSub1 = q.subtract(ONE);
+ phi = pSub1.multiply(qSub1);
+ lcm = phi.divide(pSub1.gcd(qSub1));
+
//
- // calculate the modulus
+ // calculate the private exponent
//
- n = p.multiply(q);
+ d = e.modInverse(lcm);
- if (n.bitLength() == param.getStrength())
+ // if d is less than or equal to dLowerBound, we need to start over
+ // also, for backward compatibility, if d is not the same as
+ // e.modInverse(phi), we need to start over
+
+ if (d.bitLength() <= qbitlength || !d.equals(e.modInverse(phi)))
{
- break;
- }
+ continue;
+ }
+ else
+ {
+ done = true;
+ }
//
- // if we get here our primes aren't big enough, make the largest
- // of the two p and try again
+ // calculate the CRT factors
//
- p = p.max(q);
- }
+ BigInteger dP, dQ, qInv;
- if (p.compareTo(q) < 0)
- {
- phi = p;
- p = q;
- q = phi;
- }
-
- pSub1 = p.subtract(ONE);
- qSub1 = q.subtract(ONE);
- phi = pSub1.multiply(qSub1);
+ dP = d.remainder(pSub1);
+ dQ = d.remainder(qSub1);
+ qInv = q.modInverse(p);
- //
- // calculate the private exponent
- //
- d = e.modInverse(phi);
+ result = new AsymmetricCipherKeyPair(
+ new RSAKeyParameters(false, n, e),
+ new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+ }
- //
- // calculate the CRT factors
- //
- BigInteger dP, dQ, qInv;
+ return result;
+ }
- dP = d.remainder(pSub1);
- dQ = d.remainder(qSub1);
- qInv = q.modInverse(p);
+ /**
+ * Choose a random prime value for use with RSA
+ *
+ * @param bitlength the bit-length of the returned prime
+ * @param e the RSA public exponent
+ * @return a prime p, with (p-1) relatively prime to e
+ */
+ protected BigInteger chooseRandomPrime(int bitlength, BigInteger e)
+ {
+ for (;;)
+ {
+ BigInteger p = new BigInteger(bitlength, 1, param.getRandom());
+
+ if (p.mod(e).equals(ONE))
+ {
+ continue;
+ }
+
+ if (!p.isProbablePrime(param.getCertainty()))
+ {
+ continue;
+ }
- return new AsymmetricCipherKeyPair(
- new RSAKeyParameters(false, n, e),
- new RSAPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+ if (!e.gcd(p.subtract(ONE)).equals(ONE))
+ {
+ continue;
+ }
+
+ return p;
+ }
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java
index da22fa48..0b3dc147 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/SCrypt.java
@@ -4,14 +4,65 @@ import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.engines.Salsa20Engine;
import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Arrays;
-
+import org.bouncycastle.util.Pack;
+
+/**
+ * Implementation of the scrypt a password-based key derivation function.
+ * <p>
+ * Scrypt was created by Colin Percival and is specified in <a
+ * href="http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01">draft-josefsson-scrypt-kd</a>
+ *
+ */
public class SCrypt
{
- // TODO Validate arguments
+ /**
+ * Generate a key using the scrypt key derivation function.
+ *
+ * @param P the bytes of the pass phrase.
+ * @param S the salt to use for this invocation.
+ * @param N CPU/Memory cost parameter. Must be larger than 1, a power of 2 and less than
+ * <code>2^(128 * r / 8)</code>.
+ * @param r the block size, must be >= 1.
+ * @param p Parallelization parameter. Must be a positive integer less than or equal to
+ * <code>Integer.MAX_VALUE / (128 * r * 8)</code>.
+ *
+ * @param dkLen the length of the key to generate.
+ * @return the generated key.
+ */
public static byte[] generate(byte[] P, byte[] S, int N, int r, int p, int dkLen)
{
+ if (P== null)
+ {
+ throw new IllegalArgumentException("Passphrase P must be provided.");
+ }
+ if (S == null)
+ {
+ throw new IllegalArgumentException("Salt S must be provided.");
+ }
+ if (N <= 1)
+ {
+ throw new IllegalArgumentException("Cost parameter N must be > 1.");
+ }
+ // Only value of r that cost (as an int) could be exceeded for is 1
+ if (r == 1 && N > 65536)
+ {
+ throw new IllegalArgumentException("Cost parameter N must be > 1 and < 65536.");
+ }
+ if (r < 1)
+ {
+ throw new IllegalArgumentException("Block size r must be >= 1.");
+ }
+ int maxParallel = Integer.MAX_VALUE / (128 * r * 8);
+ if (p < 1 || p > maxParallel)
+ {
+ throw new IllegalArgumentException("Parallelisation parameter p must be >= 1 and <= " + maxParallel
+ + " (based on block size r of " + r + ")");
+ }
+ if (dkLen < 1)
+ {
+ throw new IllegalArgumentException("Generated key length dkLen must be >= 1.");
+ }
return MFcrypt(P, S, N, r, p, dkLen);
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html
new file mode 100644
index 00000000..9d73ce3e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html
@@ -0,0 +1,5 @@
+<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/CipherIOException.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherIOException.java
new file mode 100644
index 00000000..beeb60bc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherIOException.java
@@ -0,0 +1,26 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.IOException;
+
+/**
+ * {@link IOException} wrapper around an exception indicating a problem with the use of a cipher.
+ */
+public class CipherIOException
+ extends IOException
+{
+ private static final long serialVersionUID = 1L;
+
+ private final Throwable cause;
+
+ public CipherIOException(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/CipherInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
index 93b04e99..b06d1f53 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
@@ -6,14 +6,16 @@ import java.io.InputStream;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.SkippingCipher;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.util.Arrays;
/**
* 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/>
+ * <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.
@@ -21,18 +23,24 @@ import org.bouncycastle.crypto.modes.AEADBlockCipher;
public class CipherInputStream
extends FilterInputStream
{
+ private static final int INPUT_BUF_SIZE = 2048;
+
+ private SkippingCipher skippingCipher;
+ private byte[] inBuf;
+
private BufferedBlockCipher bufferedBlockCipher;
private StreamCipher streamCipher;
private AEADBlockCipher aeadBlockCipher;
- private final byte[] buf;
- private final byte[] inBuf;
+ private byte[] buf;
+ private byte[] markBuf;
+
private int bufOff;
private int maxBuf;
private boolean finalized;
-
- private static final int INPUT_BUF_SIZE = 2048;
+ private long markPosition;
+ private int markBufOff;
/**
* Constructs a CipherInputStream from an InputStream and a
@@ -42,37 +50,73 @@ public class CipherInputStream
InputStream is,
BufferedBlockCipher cipher)
{
+ this(is, cipher, INPUT_BUF_SIZE);
+ }
+
+ /**
+ * Constructs a CipherInputStream from an InputStream and a StreamCipher.
+ */
+ public CipherInputStream(
+ InputStream is,
+ StreamCipher cipher)
+ {
+ this(is, cipher, INPUT_BUF_SIZE);
+ }
+
+ /**
+ * Constructs a CipherInputStream from an InputStream and an AEADBlockCipher.
+ */
+ public CipherInputStream(
+ InputStream is,
+ AEADBlockCipher cipher)
+ {
+ this(is, cipher, INPUT_BUF_SIZE);
+ }
+
+ /**
+ * Constructs a CipherInputStream from an InputStream, a
+ * BufferedBlockCipher, and a specified internal buffer size.
+ */
+ public CipherInputStream(
+ InputStream is,
+ BufferedBlockCipher cipher,
+ int bufSize)
+ {
super(is);
this.bufferedBlockCipher = cipher;
-
- buf = new byte[cipher.getOutputSize(INPUT_BUF_SIZE)];
- inBuf = new byte[INPUT_BUF_SIZE];
+ this.inBuf = new byte[bufSize];
+ this.skippingCipher = (cipher instanceof SkippingCipher) ? (SkippingCipher)cipher : null;
}
+ /**
+ * Constructs a CipherInputStream from an InputStream, a StreamCipher, and a specified internal buffer size.
+ */
public CipherInputStream(
InputStream is,
- StreamCipher cipher)
+ StreamCipher cipher,
+ int bufSize)
{
super(is);
this.streamCipher = cipher;
-
- buf = new byte[INPUT_BUF_SIZE];
- inBuf = new byte[INPUT_BUF_SIZE];
+ this.inBuf = new byte[bufSize];
+ this.skippingCipher = (cipher instanceof SkippingCipher) ? (SkippingCipher)cipher : null;
}
/**
- * Constructs a CipherInputStream from an InputStream and an AEADBlockCipher.
+ * Constructs a CipherInputStream from an InputStream, an AEADBlockCipher, and a specified internal buffer size.
*/
- public CipherInputStream(InputStream is, AEADBlockCipher cipher)
+ public CipherInputStream(
+ InputStream is,
+ AEADBlockCipher cipher,
+ int bufSize)
{
super(is);
this.aeadBlockCipher = cipher;
-
- buf = new byte[cipher.getOutputSize(INPUT_BUF_SIZE)];
- inBuf = new byte[INPUT_BUF_SIZE];
+ this.inBuf = new byte[bufSize];
+ this.skippingCipher = (cipher instanceof SkippingCipher) ? (SkippingCipher)cipher : null;
}
/**
@@ -108,6 +152,7 @@ public class CipherInputStream
try
{
+ ensureCapacity(read, false);
if (bufferedBlockCipher != null)
{
maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, read, buf, 0);
@@ -124,7 +169,7 @@ public class CipherInputStream
}
catch (Exception e)
{
- throw new IOException("Error processing stream " + e);
+ throw new CipherIOException("Error processing stream ", e);
}
}
return maxBuf;
@@ -136,6 +181,7 @@ public class CipherInputStream
try
{
finalized = true;
+ ensureCapacity(0, true);
if (bufferedBlockCipher != null)
{
maxBuf = bufferedBlockCipher.doFinal(buf, 0);
@@ -162,9 +208,9 @@ public class CipherInputStream
/**
* Reads data from the underlying stream and processes it with the cipher until the cipher
* outputs data, and returns the next available byte.
- * <p/>
+ * <p>
* If the underlying stream is exhausted by this call, the cipher will be finalised.
- *
+ * </p>
* @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).
@@ -186,9 +232,9 @@ public class CipherInputStream
/**
* 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/>
+ * <p>
* If the underlying stream is exhausted by this call, the cipher will be finalised.
- *
+ * </p>
* @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.
@@ -206,9 +252,9 @@ public class CipherInputStream
/**
* 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/>
+ * <p>
* If the underlying stream is exhausted by this call, the cipher will be finalised.
- *
+ * </p>
* @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.
@@ -247,9 +293,36 @@ public class CipherInputStream
return 0;
}
- int skip = (int)Math.min(n, available());
- bufOff += skip;
- return skip;
+ if (skippingCipher != null)
+ {
+ int avail = available();
+ if (n <= avail)
+ {
+ bufOff += n;
+
+ return n;
+ }
+
+ bufOff = maxBuf;
+
+ long skip = in.skip(n - avail);
+
+ long cSkip = skippingCipher.skip(skip);
+
+ if (skip != cSkip)
+ {
+ throw new IOException("Unable to skip cipher " + skip + " bytes.");
+ }
+
+ return skip + avail;
+ }
+ else
+ {
+ int skip = (int)Math.min(n, available());
+ bufOff += skip;
+
+ return skip;
+ }
}
public int available()
@@ -259,11 +332,49 @@ public class CipherInputStream
}
/**
+ * Ensure the cipher text buffer has space sufficient to accept an upcoming output.
+ *
+ * @param updateSize the size of the pending update.
+ * @param finalOutput <code>true</code> iff this the cipher is to be finalised.
+ */
+ private void ensureCapacity(int updateSize, boolean finalOutput)
+ {
+ int bufLen = updateSize;
+ if (finalOutput)
+ {
+ if (bufferedBlockCipher != null)
+ {
+ bufLen = bufferedBlockCipher.getOutputSize(updateSize);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ bufLen = aeadBlockCipher.getOutputSize(updateSize);
+ }
+ }
+ else
+ {
+ if (bufferedBlockCipher != null)
+ {
+ bufLen = bufferedBlockCipher.getUpdateOutputSize(updateSize);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ bufLen = aeadBlockCipher.getUpdateOutputSize(updateSize);
+ }
+ }
+
+ if ((buf == null) || (buf.length < bufLen))
+ {
+ buf = new byte[bufLen];
+ }
+ }
+
+ /**
* 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).
+ * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
*/
public void close()
throws IOException
@@ -282,19 +393,84 @@ public class CipherInputStream
}
}
maxBuf = bufOff = 0;
+ markBufOff = 0;
+ markPosition = 0;
+ if (markBuf != null)
+ {
+ Arrays.fill(markBuf, (byte)0);
+ markBuf = null;
+ }
+ if (buf != null)
+ {
+ Arrays.fill(buf, (byte)0);
+ buf = null;
+ }
+ Arrays.fill(inBuf, (byte)0);
}
+ /**
+ * Mark the current position.
+ * <p>
+ * This method only works if markSupported() returns true - which means the underlying stream supports marking, and the cipher passed
+ * in to this stream's constructor is a SkippingCipher (so capable of being reset to an arbitrary point easily).
+ * </p>
+ * @param readlimit the maximum read ahead required before a reset() may be called.
+ */
public void mark(int readlimit)
{
+ in.mark(readlimit);
+ if (skippingCipher != null)
+ {
+ markPosition = skippingCipher.getPosition();
+ }
+
+ if (buf != null)
+ {
+ markBuf = new byte[buf.length];
+ System.arraycopy(buf, 0, markBuf, 0, buf.length);
+ }
+
+ markBufOff = bufOff;
}
+ /**
+ * Reset to the last marked position, if supported.
+ *
+ * @throws IOException if marking not supported by the cipher used, or the underlying stream.
+ */
public void reset()
throws IOException
{
- }
+ if (skippingCipher == null)
+ {
+ throw new IOException("cipher must implement SkippingCipher to be used with reset()");
+ }
+
+ in.reset();
+
+ skippingCipher.seekTo(markPosition);
+
+ if (markBuf != null)
+ {
+ buf = markBuf;
+ }
+ bufOff = markBufOff;
+ }
+
+ /**
+ * Return true if mark(readlimit) is supported. This will be true if the underlying stream supports marking and the
+ * cipher used is a SkippingCipher,
+ *
+ * @return true if mark(readlimit) supported, false otherwise.
+ */
public boolean markSupported()
{
+ if (skippingCipher != null)
+ {
+ return in.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 9beb5b95..5d5f0d11 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
@@ -14,7 +14,7 @@ import org.bouncycastle.crypto.modes.AEADBlockCipher;
* 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/>
+ * <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.
*/
@@ -86,7 +86,7 @@ public class CipherOutputStream
/**
* 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
@@ -118,7 +118,7 @@ public class CipherOutputStream
int len)
throws IOException
{
- ensureCapacity(len);
+ ensureCapacity(len, false);
if (bufferedBlockCipher != null)
{
@@ -149,24 +149,35 @@ public class CipherOutputStream
/**
* Ensure the ciphertext buffer has space sufficient to accept an upcoming output.
*
- * @param outputSize the size of the pending update.
+ * @param updateSize the size of the pending update.
+ * @param finalOutput <code>true</code> iff this the cipher is to be finalised.
*/
- private void ensureCapacity(int outputSize)
+ private void ensureCapacity(int updateSize, boolean finalOutput)
{
- // This overestimates buffer on updates for AEAD/padded, but keeps it simple.
- int bufLen;
- if (bufferedBlockCipher != null)
+ int bufLen = updateSize;
+ if (finalOutput)
{
- bufLen = bufferedBlockCipher.getOutputSize(outputSize);
- }
- else if (aeadBlockCipher != null)
- {
- bufLen = aeadBlockCipher.getOutputSize(outputSize);
+ if (bufferedBlockCipher != null)
+ {
+ bufLen = bufferedBlockCipher.getOutputSize(updateSize);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ bufLen = aeadBlockCipher.getOutputSize(updateSize);
+ }
}
else
{
- bufLen = outputSize;
+ if (bufferedBlockCipher != null)
+ {
+ bufLen = bufferedBlockCipher.getUpdateOutputSize(updateSize);
+ }
+ else if (aeadBlockCipher != null)
+ {
+ bufLen = aeadBlockCipher.getUpdateOutputSize(updateSize);
+ }
}
+
if ((buf == null) || (buf.length < bufLen))
{
buf = new byte[bufLen];
@@ -177,8 +188,7 @@ public class CipherOutputStream
* 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
@@ -196,12 +206,12 @@ public class CipherOutputStream
/**
* 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.
@@ -213,7 +223,7 @@ public class CipherOutputStream
public void close()
throws IOException
{
- ensureCapacity(0);
+ ensureCapacity(0, true);
IOException error = null;
try
{
@@ -242,7 +252,7 @@ public class CipherOutputStream
}
catch (Exception e)
{
- error = new IOException("Error closing stream: " + e);
+ error = new CipherIOException("Error closing stream: ", e);
}
try
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java
index b601d4c1..46561c67 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java
@@ -8,21 +8,12 @@ import java.io.IOException;
* expose invalid ciphertext errors.
*/
public class InvalidCipherTextIOException
- extends IOException
+ extends CipherIOException
{
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;
+ super(message, 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
new file mode 100644
index 00000000..f2c9e406
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html
@@ -0,0 +1,5 @@
+<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 82ac20c2..35131982 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
@@ -13,7 +13,10 @@ 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.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
+import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
/**
@@ -120,8 +123,10 @@ public class ECIESKeyEncapsulation
// Compute the static-ephemeral key agreement
BigInteger rPrime = CofactorMode ? r.multiply(h).mod(n) : r;
+ ECMultiplier basePointMultiplier = createBasePointMultiplier();
+
ECPoint[] ghTilde = new ECPoint[]{
- ecParams.getG().multiply(r),
+ basePointMultiplier.multiply(ecParams.getG(), r),
ecPubKey.getQ().multiply(rPrime)
};
@@ -131,33 +136,13 @@ public class ECIESKeyEncapsulation
ECPoint gTilde = ghTilde[0], hTilde = ghTilde[1];
// Encode the ephemeral public key
- byte[] C = gTilde.getEncoded();
+ byte[] C = gTilde.getEncoded(false);
System.arraycopy(C, 0, out, outOff, C.length);
// Encode the shared secret value
byte[] PEH = hTilde.getAffineXCoord().getEncoded();
- // Initialise the KDF
- byte[] kdfInput;
- if (SingleHashMode)
- {
- kdfInput = new byte[C.length + PEH.length];
- System.arraycopy(C, 0, kdfInput, 0, C.length);
- System.arraycopy(PEH, 0, kdfInput, C.length, PEH.length);
- }
- else
- {
- kdfInput = PEH;
- }
-
- kdf.init(new KDFParameters(kdfInput, null));
-
- // Generate the secret key
- byte[] K = new byte[keyLen];
- kdf.generateBytes(K, 0, K.length);
-
- // Return the ciphertext
- return new KeyParameter(K);
+ return deriveKey(keyLen, C, PEH);
}
/**
@@ -220,25 +205,7 @@ public class ECIESKeyEncapsulation
// Encode the shared secret value
byte[] PEH = hTilde.getAffineXCoord().getEncoded();
- // Initialise the KDF
- byte[] kdfInput;
- if (SingleHashMode)
- {
- kdfInput = new byte[C.length + PEH.length];
- System.arraycopy(C, 0, kdfInput, 0, C.length);
- System.arraycopy(PEH, 0, kdfInput, C.length, PEH.length);
- }
- else
- {
- kdfInput = PEH;
- }
- kdf.init(new KDFParameters(kdfInput, null));
-
- // Generate the secret key
- byte[] K = new byte[keyLen];
- kdf.generateBytes(K, 0, K.length);
-
- return new KeyParameter(K);
+ return deriveKey(keyLen, C, PEH);
}
/**
@@ -252,4 +219,36 @@ public class ECIESKeyEncapsulation
{
return decrypt(in, 0, in.length, keyLen);
}
+
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
+
+ protected KeyParameter deriveKey(int keyLen, byte[] C, byte[] PEH)
+ {
+ byte[] kdfInput = PEH;
+ if (SingleHashMode)
+ {
+ kdfInput = Arrays.concatenate(C, PEH);
+ Arrays.fill(PEH, (byte)0);
+ }
+
+ try
+ {
+ // Initialise the KDF
+ kdf.init(new KDFParameters(kdfInput, null));
+
+ // Generate the secret key
+ byte[] K = new byte[keyLen];
+ kdf.generateBytes(K, 0, K.length);
+
+ // Return the ciphertext
+ return new KeyParameter(K);
+ }
+ finally
+ {
+ Arrays.fill(kdfInput, (byte)0);
+ }
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java b/bcprov/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java
index 8c1a1724..42fc2353 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java
@@ -38,7 +38,6 @@ public class RSAKeyEncapsulation
this.rnd = rnd;
}
-
/**
* Initialise the RSA-KEM.
*
@@ -51,12 +50,9 @@ public class RSAKeyEncapsulation
{
throw new IllegalArgumentException("RSA key required");
}
- else
- {
- this.key = (RSAKeyParameters)key;
- }
- }
+ this.key = (RSAKeyParameters)key;
+ }
/**
* Generate and encapsulate a random session key.
@@ -79,25 +75,15 @@ public class RSAKeyEncapsulation
// Generate the ephemeral random and encode it
BigInteger r = BigIntegers.createRandomInRange(ZERO, n.subtract(ONE), rnd);
- byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength() + 7) / 8, r);
// Encrypt the random and encode it
BigInteger c = r.modPow(e, n);
byte[] C = BigIntegers.asUnsignedByteArray((n.bitLength() + 7) / 8, c);
System.arraycopy(C, 0, out, outOff, C.length);
-
- // Initialise the KDF
- kdf.init(new KDFParameters(R, null));
-
- // Generate the secret key
- byte[] K = new byte[keyLen];
- kdf.generateBytes(K, 0, K.length);
-
- return new KeyParameter(K);
+ return generateKey(n, r, keyLen);
}
-
/**
* Generate and encapsulate a random session key.
*
@@ -110,7 +96,6 @@ public class RSAKeyEncapsulation
return encrypt(out, 0, keyLen);
}
-
/**
* Decrypt an encapsulated session key.
*
@@ -138,16 +123,8 @@ public class RSAKeyEncapsulation
// Decrypt the ephemeral random and encode it
BigInteger r = c.modPow(d, n);
- byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength() + 7) / 8, r);
- // Initialise the KDF
- kdf.init(new KDFParameters(R, null));
-
- // Generate the secret key
- byte[] K = new byte[keyLen];
- kdf.generateBytes(K, 0, K.length);
-
- return new KeyParameter(K);
+ return generateKey(n, r, keyLen);
}
/**
@@ -161,4 +138,18 @@ public class RSAKeyEncapsulation
{
return decrypt(in, 0, in.length, keyLen);
}
+
+ protected KeyParameter generateKey(BigInteger n, BigInteger r, int keyLen)
+ {
+ byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength() + 7) / 8, r);
+
+ // Initialise the KDF
+ kdf.init(new KDFParameters(R, null));
+
+ // Generate the secret key
+ byte[] K = new byte[keyLen];
+ kdf.generateBytes(K, 0, K.length);
+
+ return new KeyParameter(K);
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html
new file mode 100644
index 00000000..88fddd0e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Key Encapsulation Mechanisms.
+</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 1aa8ede4..0492ae69 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java
@@ -58,14 +58,14 @@ public class CMac implements Mac
/**
* create a standard MAC based on a block cipher with the size of the
* MAC been given in bits.
- * <p/>
+ * <p>
* Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
* or 16 bits if being used as a data authenticator (FIPS Publication 113),
* and in general should be less than the size of the block cipher as it reduces
* the chance of an exhaustive attack (see Handbook of Applied Cryptography).
*
* @param cipher the cipher to be used as the basis of the MAC generation.
- * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8 and <= 128.
+ * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8 and &lt;= 128.
*/
public CMac(BlockCipher cipher, int macSizeInBits)
{
@@ -133,24 +133,31 @@ public class CMac implements Mac
public void init(CipherParameters params)
{
- if (params instanceof KeyParameter)
- {
- cipher.init(true, params);
-
- //initializes the L, Lu, Lu2 numbers
- L = new byte[ZEROES.length];
- 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.");
- }
+ validate(params);
+
+ cipher.init(true, params);
+
+ //initializes the L, Lu, Lu2 numbers
+ L = new byte[ZEROES.length];
+ cipher.processBlock(ZEROES, 0, L, 0);
+ Lu = doubleLu(L);
+ Lu2 = doubleLu(Lu);
reset();
}
+ void validate(CipherParameters params)
+ {
+ if (params != null)
+ {
+ if (!(params instanceof KeyParameter))
+ {
+ // CMAC mode does not permit IV to underlying CBC mode
+ throw new IllegalArgumentException("CMac mode only permits key to be set.");
+ }
+ }
+ }
+
public int getMacSize()
{
return macSize;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMacWithIV.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMacWithIV.java
new file mode 100644
index 00000000..a0371d95
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMacWithIV.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+
+/**
+ * A non-NIST variant which allows passing of an IV to the underlying CBC cipher.
+ * <p>Note: there isn't really a good reason to use an IV here, use the regular CMac where possible.</p>
+ */
+public class CMacWithIV
+ extends CMac
+{
+ public CMacWithIV(BlockCipher cipher)
+ {
+ super(cipher);
+ }
+
+ public CMacWithIV(BlockCipher cipher, int macSizeInBits)
+ {
+ super(cipher, macSizeInBits);
+ }
+
+ void validate(CipherParameters params)
+ {
+ // accept all
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/GMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/GMac.java
index 8aae1e26..b34f9ea5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/GMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/GMac.java
@@ -23,7 +23,7 @@ public class GMac implements Mac
/**
* Creates a GMAC based on the operation of a block cipher in GCM mode.
- * <p/>
+ * <p>
* This will produce an authentication code the length of the block size of the cipher.
*
* @param cipher
@@ -40,7 +40,8 @@ public class GMac implements Mac
* Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode.
*
* @param macSizeBits
- * the mac size to generate, in bits. Must be a multiple of 8 and >= 96 and <= 128.
+ * the mac size to generate, in bits. Must be a multiple of 8 and &gt;= 32 and &lt;= 128.
+ * Sizes less than 96 are not recommended, but are supported for specialized applications.
* @param cipher
* the cipher to be used in GCM mode to generate the MAC.
*/
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
index 150eb617..7a346f1e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
@@ -7,7 +7,7 @@ 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;
+import org.bouncycastle.util.Pack;
/**
* Poly1305 message authentication code, designed by D. J. Bernstein.
@@ -53,6 +53,14 @@ public class Poly1305
private int h0, h1, h2, h3, h4;
/**
+ * Constructs a Poly1305 MAC, where the key passed to init() will be used directly.
+ */
+ public Poly1305()
+ {
+ this.cipher = null;
+ }
+
+ /**
* Constructs a Poly1305 MAC, using a 128 bit block cipher.
*/
public Poly1305(final BlockCipher cipher)
@@ -66,35 +74,48 @@ public class Poly1305
/**
* 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}.
+ *
+ * @param params if used with a block cipher, then 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}, otherwise just the
+ * {@link KeyParameter}.
*/
- public void init(final CipherParameters params)
+ public void init(CipherParameters params)
throws IllegalArgumentException
{
- final byte[] nonce;
- final byte[] key;
- if ((params instanceof ParametersWithIV) && ((ParametersWithIV)params).getParameters() instanceof KeyParameter)
+ byte[] nonce = null;
+
+ if (cipher != null)
{
- nonce = ((ParametersWithIV)params).getIV();
- key = ((KeyParameter)((ParametersWithIV)params).getParameters()).getKey();
+ if (!(params instanceof ParametersWithIV))
+ {
+ throw new IllegalArgumentException("Poly1305 requires an IV when used with a block cipher.");
+ }
+
+ ParametersWithIV ivParams = (ParametersWithIV)params;
+ nonce = ivParams.getIV();
+ params = ivParams.getParameters();
}
- else
+
+ if (!(params instanceof KeyParameter))
{
- throw new IllegalArgumentException("Poly1305 requires a key and and IV.");
+ throw new IllegalArgumentException("Poly1305 requires a key.");
}
- setKey(key, nonce);
+ KeyParameter keyParams = (KeyParameter)params;
+
+ setKey(keyParams.getKey(), nonce);
+
reset();
}
private void setKey(final byte[] key, final byte[] nonce)
{
- if (nonce.length != BLOCK_SIZE)
+ if (cipher != null && (nonce == null || nonce.length != BLOCK_SIZE))
{
throw new IllegalArgumentException("Poly1305 requires a 128 bit IV.");
}
+
Poly1305KeyGenerator.checkKey(key);
// Extract r portion of key
@@ -115,22 +136,28 @@ public class Poly1305
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);
+ final byte[] kBytes;
+ if (cipher == null)
+ {
+ kBytes = key;
+ }
+ else
+ {
+ // Compute encrypted nonce
+ kBytes = new byte[BLOCK_SIZE];
+ cipher.init(true, new KeyParameter(key, 0, BLOCK_SIZE));
+ cipher.processBlock(nonce, 0, kBytes, 0);
+ }
- k0 = Pack.littleEndianToInt(cipherKey, 0);
- k1 = Pack.littleEndianToInt(cipherKey, 4);
- k2 = Pack.littleEndianToInt(cipherKey, 8);
- k3 = Pack.littleEndianToInt(cipherKey, 12);
+ k0 = Pack.littleEndianToInt(kBytes, 0);
+ k1 = Pack.littleEndianToInt(kBytes, 4);
+ k2 = Pack.littleEndianToInt(kBytes, 8);
+ k3 = Pack.littleEndianToInt(kBytes, 12);
}
public String getAlgorithmName()
{
- return "Poly1305-" + cipher.getAlgorithmName();
+ return cipher == null ? "Poly1305" : "Poly1305-" + cipher.getAlgorithmName();
}
public int getMacSize()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/SipHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SipHash.java
index 527c8040..d6b9dbb8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/SipHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SipHash.java
@@ -4,13 +4,12 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.util.Pack;
-import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
/**
* Implementation of SipHash as specified in "SipHash: a fast short-input PRF", by Jean-Philippe
* Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf).
- * <p/>
+ * <p>
* "SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d are the number of
* compression rounds and the number of finalization rounds. A compression round is identical to a
* finalization round and this round function is called SipRound. Given a 128-bit key k and a
@@ -19,14 +18,13 @@ import org.bouncycastle.util.Arrays;
public class SipHash
implements Mac
{
-
protected final int c, d;
protected long k0, k1;
- protected long v0, v1, v2, v3, v4;
+ protected long v0, v1, v2, v3;
- protected byte[] buf = new byte[8];
- protected int bufPos = 0;
+ protected long m = 0;
+ protected int wordPos = 0;
protected int wordCount = 0;
/**
@@ -34,7 +32,7 @@ public class SipHash
*/
public SipHash()
{
- // use of this confuses flow analyser on earlier JDKs.
+ // use of 'this' confuses the flow analyser on earlier JDKs.
this.c = 2;
this.d = 4;
}
@@ -84,12 +82,13 @@ public class SipHash
public void update(byte input)
throws IllegalStateException
{
+ m >>>= 8;
+ m |= (input & 0xffL) << 56;
- buf[bufPos] = input;
- if (++bufPos == buf.length)
+ if (++wordPos == 8)
{
processMessageWord();
- bufPos = 0;
+ wordPos = 0;
}
}
@@ -97,14 +96,41 @@ public class SipHash
throws DataLengthException,
IllegalStateException
{
-
- for (int i = 0; i < length; ++i)
+ int i = 0, fullWords = length & ~7;
+ if (wordPos == 0)
{
- buf[bufPos] = input[offset + i];
- if (++bufPos == buf.length)
+ for (; i < fullWords; i += 8)
{
+ m = Pack.littleEndianToLong(input, offset + i);
processMessageWord();
- bufPos = 0;
+ }
+ for (; i < length; ++i)
+ {
+ m >>>= 8;
+ m |= (input[offset + i] & 0xffL) << 56;
+ }
+ wordPos = length - fullWords;
+ }
+ else
+ {
+ int bits = wordPos << 3;
+ for (; i < fullWords; i += 8)
+ {
+ long n = Pack.littleEndianToLong(input, offset + i);
+ m = (n << bits) | (m >>> -bits);
+ processMessageWord();
+ m = n;
+ }
+ for (; i < length; ++i)
+ {
+ m >>>= 8;
+ m |= (input[offset + i] & 0xffL) << 56;
+
+ if (++wordPos == 8)
+ {
+ processMessageWord();
+ wordPos = 0;
+ }
}
}
}
@@ -112,12 +138,10 @@ public class SipHash
public long doFinal()
throws DataLengthException, IllegalStateException
{
-
- buf[7] = (byte)(((wordCount << 3) + bufPos) & 0xff);
- while (bufPos < 7)
- {
- buf[bufPos++] = 0;
- }
+ // NOTE: 2 distinct shifts to avoid "64-bit shift" when wordPos == 0
+ m >>>= ((7 - wordPos) << 3);
+ m >>>= 8;
+ m |= (((wordCount << 3) + wordPos) & 0xffL) << 56;
processMessageWord();
@@ -135,7 +159,6 @@ public class SipHash
public int doFinal(byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
-
long result = doFinal();
Pack.longToLittleEndian(result, out, outOff);
return 8;
@@ -143,22 +166,19 @@ public class SipHash
public void reset()
{
-
v0 = k0 ^ 0x736f6d6570736575L;
v1 = k1 ^ 0x646f72616e646f6dL;
v2 = k0 ^ 0x6c7967656e657261L;
v3 = k1 ^ 0x7465646279746573L;
- Arrays.fill(buf, (byte)0);
- bufPos = 0;
+ m = 0;
+ wordPos = 0;
wordCount = 0;
}
protected void processMessageWord()
{
-
++wordCount;
- long m = Pack.littleEndianToLong(buf, 0);
v3 ^= m;
applySipRounds(c);
v0 ^= m;
@@ -166,27 +186,31 @@ public class SipHash
protected void applySipRounds(int n)
{
+ long r0 = v0, r1 = v1, r2 = v2, r3 = v3;
+
for (int r = 0; r < n; ++r)
{
- v0 += v1;
- v2 += v3;
- v1 = rotateLeft(v1, 13);
- v3 = rotateLeft(v3, 16);
- v1 ^= v0;
- v3 ^= v2;
- v0 = rotateLeft(v0, 32);
- v2 += v1;
- v0 += v3;
- v1 = rotateLeft(v1, 17);
- v3 = rotateLeft(v3, 21);
- v1 ^= v2;
- v3 ^= v0;
- v2 = rotateLeft(v2, 32);
+ r0 += r1;
+ r2 += r3;
+ r1 = rotateLeft(r1, 13);
+ r3 = rotateLeft(r3, 16);
+ r1 ^= r0;
+ r3 ^= r2;
+ r0 = rotateLeft(r0, 32);
+ r2 += r1;
+ r0 += r3;
+ r1 = rotateLeft(r1, 17);
+ r3 = rotateLeft(r3, 21);
+ r1 ^= r2;
+ r3 ^= r0;
+ r2 = rotateLeft(r2, 32);
}
+
+ v0 = r0; v1 = r1; v2 = r2; v3 = r3;
}
protected static long rotateLeft(long x, int n)
{
- return (x << n) | (x >>> (64 - n));
+ return (x << n) | (x >>> -n);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
index 60972470..7115b510 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
@@ -10,13 +10,12 @@ 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/>
+ * <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/>
+ * <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
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html
new file mode 100644
index 00000000..0b1f86dd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Classes for creating MACs and HMACs.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
index 71b75954..fe461196 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
@@ -7,6 +7,16 @@ import org.bouncycastle.crypto.InvalidCipherTextException;
/**
* A block cipher mode that includes authenticated encryption with a streaming mode and optional associated data.
+ * <p>
+ * Implementations of this interface may operate in a packet mode (where all input data is buffered and
+ * processed dugin the call to {@link #doFinal(byte[], int)}), or in a streaming mode (where output data is
+ * incrementally produced with each call to {@link #processByte(byte, byte[], int)} or
+ * {@link #processBytes(byte[], int, int, byte[], int)}.
+ * </p>
+ * This is important to consider during decryption: in a streaming mode, unauthenticated plaintext data
+ * may be output prior to the call to {@link #doFinal(byte[], int)} that results in an authentication
+ * failure. The higher level protocol utilising this cipher must ensure the plaintext data is handled
+ * appropriately until the end of data is reached and the entire ciphertext is authenticated.
* @see org.bouncycastle.crypto.params.AEADParameters
*/
public interface AEADBlockCipher
@@ -101,6 +111,11 @@ public interface AEADBlockCipher
/**
* return the size of the output buffer required for a processBytes
* an input of len bytes.
+ * <p>
+ * The returned size may be dependent on the initialisation of this cipher
+ * and may not be accurate once subsequent input data is processed - this method
+ * should be invoked immediately prior to input data being processed.
+ * </p>
*
* @param len the length of the input.
* @return the space required to accommodate a call to processBytes
@@ -111,7 +126,12 @@ public interface AEADBlockCipher
/**
* return the size of the output buffer required for a processBytes plus a
* doFinal with an input of len bytes.
- *
+ * <p>
+ * The returned size may be dependent on the initialisation of this cipher
+ * and may not be accurate once subsequent input data is processed - this method
+ * should be invoked immediately prior to a call to final processing of input data
+ * and a call to {@link #doFinal(byte[], int)}.
+ * </p>
* @param len the length of the input.
* @return the space required to accommodate a call to processBytes and doFinal
* with len bytes of input.
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 fef51fdb..7f870ca2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -7,6 +7,7 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.ParametersWithIV;
@@ -42,7 +43,7 @@ public class CCMBlockCipher
this.cipher = c;
this.blockSize = c.getBlockSize();
this.macBlock = new byte[blockSize];
-
+
if (blockSize != 16)
{
throw new IllegalArgumentException("cipher required with a block size of 16.");
@@ -99,7 +100,7 @@ public class CCMBlockCipher
{
throw new IllegalArgumentException("nonce must have length from 7 to 13 octets");
}
-
+
reset();
}
@@ -130,6 +131,10 @@ public class CCMBlockCipher
public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
+ if (in.length < (inOff + inLen))
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
data.write(in, inOff, inLen);
return 0;
@@ -155,15 +160,15 @@ public class CCMBlockCipher
/**
* Returns a byte array containing the mac calculated as part of the
* last encrypt or decrypt operation.
- *
+ *
* @return the last mac calculated.
*/
public byte[] getMac()
{
byte[] mac = new byte[macSize];
-
+
System.arraycopy(macBlock, 0, mac, 0, mac.length);
-
+
return mac;
}
@@ -267,7 +272,7 @@ public class CCMBlockCipher
outputLen = inLen + macSize;
if (output.length < (outputLen + outOff))
{
- throw new DataLengthException("Output buffer too short.");
+ throw new OutputLengthException("Output buffer too short.");
}
calculateMac(in, inOff, inLen, macBlock);
@@ -300,7 +305,7 @@ public class CCMBlockCipher
outputLen = inLen - macSize;
if (output.length < (outputLen + outOff))
{
- throw new DataLengthException("Output buffer too short.");
+ throw new OutputLengthException("Output buffer too short.");
}
System.arraycopy(in, inOff + outputLen, macBlock, 0, macSize);
@@ -350,18 +355,18 @@ public class CCMBlockCipher
// build b0
//
byte[] b0 = new byte[16];
-
+
if (hasAssociatedText())
{
b0[0] |= 0x40;
}
-
+
b0[0] |= (((cMac.getMacSize() - 2) / 2) & 0x7) << 3;
b0[0] |= ((15 - nonce.length) - 1) & 0x7;
-
+
System.arraycopy(nonce, 0, b0, 1, nonce.length);
-
+
int q = dataLen;
int count = 1;
while (q > 0)
@@ -370,22 +375,22 @@ public class CCMBlockCipher
q >>>= 8;
count++;
}
-
+
cMac.update(b0, 0, b0.length);
-
+
//
// process associated text
//
if (hasAssociatedText())
{
int extra;
-
+
int textLength = getAssociatedTextLength();
if (textLength < ((1 << 16) - (1 << 8)))
{
cMac.update((byte)(textLength >> 8));
cMac.update((byte)textLength);
-
+
extra = 2;
}
else // can't go any higher than 2^32
@@ -396,7 +401,7 @@ public class CCMBlockCipher
cMac.update((byte)(textLength >> 16));
cMac.update((byte)(textLength >> 8));
cMac.update((byte)textLength);
-
+
extra = 6;
}
@@ -418,7 +423,7 @@ public class CCMBlockCipher
}
}
}
-
+
//
// add the text
//
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 a8851690..6167d256 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -3,6 +3,7 @@ package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
@@ -10,15 +11,17 @@ import org.bouncycastle.util.Arrays;
* implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
*/
public class CFBBlockCipher
- implements BlockCipher
+ extends StreamBlockCipher
{
private byte[] IV;
private byte[] cfbV;
private byte[] cfbOutV;
+ private byte[] inBuf;
private int blockSize;
private BlockCipher cipher = null;
private boolean encrypting;
+ private int byteCount;
/**
* Basic constructor.
@@ -31,22 +34,15 @@ public class CFBBlockCipher
BlockCipher cipher,
int bitBlockSize)
{
+ super(cipher);
+
this.cipher = cipher;
this.blockSize = bitBlockSize / 8;
this.IV = new byte[cipher.getBlockSize()];
this.cfbV = new byte[cipher.getBlockSize()];
this.cfbOutV = new byte[cipher.getBlockSize()];
- }
-
- /**
- * return the underlying block cipher that we are wrapping.
- *
- * @return the underlying block cipher that we are wrapping.
- */
- public BlockCipher getUnderlyingCipher()
- {
- return cipher;
+ this.inBuf = new byte[blockSize];
}
/**
@@ -117,6 +113,54 @@ public class CFBBlockCipher
return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
}
+ protected byte calculateByte(byte in)
+ throws DataLengthException, IllegalStateException
+ {
+ return (encrypting) ? encryptByte(in) : decryptByte(in);
+ }
+
+ private byte encryptByte(byte in)
+ {
+ if (byteCount == 0)
+ {
+ cipher.processBlock(cfbV, 0, cfbOutV, 0);
+ }
+
+ byte rv = (byte)(cfbOutV[byteCount] ^ in);
+ inBuf[byteCount++] = rv;
+
+ if (byteCount == blockSize)
+ {
+ byteCount = 0;
+
+ System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
+ System.arraycopy(inBuf, 0, cfbV, cfbV.length - blockSize, blockSize);
+ }
+
+ return rv;
+ }
+
+ private byte decryptByte(byte in)
+ {
+ if (byteCount == 0)
+ {
+ cipher.processBlock(cfbV, 0, cfbOutV, 0);
+ }
+
+ inBuf[byteCount] = in;
+ byte rv = (byte)(cfbOutV[byteCount++] ^ in);
+
+ if (byteCount == blockSize)
+ {
+ byteCount = 0;
+
+ System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
+ System.arraycopy(inBuf, 0, cfbV, cfbV.length - blockSize, blockSize);
+ }
+
+ return rv;
+ }
+
/**
* return the block size we are operating at.
*
@@ -147,7 +191,9 @@ public class CFBBlockCipher
int outOff)
throws DataLengthException, IllegalStateException
{
- return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
+ processBytes(in, inOff, blockSize, out, outOff);
+
+ return blockSize;
}
/**
@@ -169,31 +215,7 @@ public class CFBBlockCipher
int outOff)
throws DataLengthException, IllegalStateException
{
- if ((inOff + blockSize) > in.length)
- {
- throw new DataLengthException("input buffer too short");
- }
-
- if ((outOff + blockSize) > out.length)
- {
- throw new DataLengthException("output buffer too short");
- }
-
- cipher.processBlock(cfbV, 0, cfbOutV, 0);
-
- //
- // XOR the cfbV with the plaintext producing the ciphertext
- //
- for (int i = 0; i < blockSize; i++)
- {
- out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
- }
-
- //
- // change over the input block.
- //
- System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
- System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize, blockSize);
+ processBytes(in, inOff, blockSize, out, outOff);
return blockSize;
}
@@ -217,31 +239,7 @@ public class CFBBlockCipher
int outOff)
throws DataLengthException, IllegalStateException
{
- if ((inOff + blockSize) > in.length)
- {
- throw new DataLengthException("input buffer too short");
- }
-
- if ((outOff + blockSize) > out.length)
- {
- throw new DataLengthException("output buffer too short");
- }
-
- cipher.processBlock(cfbV, 0, cfbOutV, 0);
-
- //
- // change over the input block.
- //
- System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
- System.arraycopy(in, inOff, cfbV, cfbV.length - blockSize, blockSize);
-
- //
- // XOR the cfbV with the ciphertext producing the plaintext
- //
- for (int i = 0; i < blockSize; i++)
- {
- out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]);
- }
+ processBytes(in, inOff, blockSize, out, outOff);
return blockSize;
}
@@ -263,6 +261,8 @@ public class CFBBlockCipher
public void reset()
{
System.arraycopy(IV, 0, cfbV, 0, IV.length);
+ Arrays.fill(inBuf, (byte)0);
+ byteCount = 0;
cipher.reset();
}
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 5388b407..64b076dd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
@@ -4,6 +4,7 @@ import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.StreamBlockCipher;
/**
* A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
@@ -22,9 +23,8 @@ public class CTSBlockCipher
public CTSBlockCipher(
BlockCipher cipher)
{
- if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher) || (cipher instanceof SICBlockCipher))
+ if (cipher instanceof StreamBlockCipher)
{
- // 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");
}
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 8f740006..209d5cdb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
@@ -5,22 +5,23 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
/**
- * A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and
+ * A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and
* Efficiency - by M. Bellare, P. Rogaway, D. Wagner.
- *
+ *
* http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
- *
- * EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block
- * cipher to encrypt and authenticate data. It's on-line (the length of a
+ *
+ * EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block
+ * cipher to encrypt and authenticate data. It's on-line (the length of a
* message isn't needed to begin processing it), has good performances, it's
* simple and provably secure (provided the underlying block cipher is secure).
- *
+ *
* Of course, this implementations is NOT thread-safe.
*/
public class EAXBlockCipher
@@ -43,7 +44,7 @@ public class EAXBlockCipher
private byte[] nonceMac;
private byte[] associatedTextMac;
private byte[] macBlock;
-
+
private int macSize;
private byte[] bufBlock;
private int bufOff;
@@ -61,7 +62,6 @@ public class EAXBlockCipher
blockSize = cipher.getBlockSize();
mac = new CMac(cipher);
macBlock = new byte[blockSize];
- bufBlock = new byte[blockSize * 2];
associatedTextMac = new byte[mac.getMacSize()];
nonceMac = new byte[mac.getMacSize()];
this.cipher = new SICBlockCipher(cipher);
@@ -113,6 +113,8 @@ public class EAXBlockCipher
throw new IllegalArgumentException("invalid parameters passed to EAX");
}
+ bufBlock = new byte[forEncryption ? blockSize : (blockSize + macSize)];
+
byte[] tag = new byte[blockSize];
// Key reuse implemented in CBC mode of underlying CMac
@@ -123,9 +125,9 @@ public class EAXBlockCipher
mac.update(nonce, 0, nonce.length);
mac.doFinal(nonceMac, 0);
- // Same BlockCipher underlies this and the mac, so reuse last key on cipher
+ // Same BlockCipher underlies this and the mac, so reuse last key on cipher
cipher.init(true, new ParametersWithIV(null, nonceMac));
-
+
reset();
}
@@ -218,6 +220,11 @@ public class EAXBlockCipher
{
initCipher();
+ if (in.length < (inOff + len))
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
int resultLen = 0;
for (int i = 0; i != len; i++)
@@ -240,12 +247,11 @@ public class EAXBlockCipher
if (forEncryption)
{
- if (out.length < (outOff + extra))
+ if (out.length < (outOff + extra + macSize))
{
- throw new DataLengthException("Output buffer too short");
+ throw new OutputLengthException("Output buffer too short");
}
cipher.processBlock(bufBlock, 0, tmp, 0);
- cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
System.arraycopy(tmp, 0, out, outOff, extra);
@@ -261,6 +267,10 @@ public class EAXBlockCipher
}
else
{
+ if (out.length < (outOff + extra - macSize))
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
if (extra < macSize)
{
throw new InvalidCipherTextException("data too short");
@@ -270,7 +280,6 @@ public class EAXBlockCipher
mac.update(bufBlock, 0, extra - macSize);
cipher.processBlock(bufBlock, 0, tmp, 0);
- cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
System.arraycopy(tmp, 0, out, outOff, extra - macSize);
}
@@ -329,6 +338,10 @@ public class EAXBlockCipher
if (bufOff == bufBlock.length)
{
+ if (out.length < (outOff + blockSize))
+ {
+ throw new OutputLengthException("Output buffer is too short");
+ }
// TODO Could move the processByte(s) calls to here
// initCipher();
@@ -347,8 +360,12 @@ public class EAXBlockCipher
size = cipher.processBlock(bufBlock, 0, out, outOff);
}
- bufOff = blockSize;
- System.arraycopy(bufBlock, blockSize, bufBlock, 0, blockSize);
+ bufOff = 0;
+ if (!forEncryption)
+ {
+ System.arraycopy(bufBlock, blockSize, bufBlock, 0, macSize);
+ bufOff = macSize;
+ }
return size;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java
index 887c1697..5791e89c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java
@@ -3,6 +3,7 @@ package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.params.ParametersWithRandom;
@@ -12,7 +13,7 @@ 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
+ extends StreamBlockCipher
{
private static final byte[] C =
{
@@ -30,6 +31,8 @@ public class GCFBBlockCipher
public GCFBBlockCipher(BlockCipher engine)
{
+ super(engine);
+
this.cfbEngine = new CFBBlockCipher(engine, engine.getBlockSize() * 8);
}
@@ -61,7 +64,8 @@ public class GCFBBlockCipher
public String getAlgorithmName()
{
- return "G" + cfbEngine.getAlgorithmName();
+ String name = cfbEngine.getAlgorithmName();
+ return name.substring(0, name.indexOf('/') - 1) + "/G" + name.substring(name.indexOf('/') + 1);
}
public int getBlockSize()
@@ -72,6 +76,13 @@ public class GCFBBlockCipher
public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
+ this.processBytes(in, inOff, cfbEngine.getBlockSize(), out, outOff);
+
+ return cfbEngine.getBlockSize();
+ }
+
+ protected byte calculateByte(byte b)
+ {
if (counter > 0 && counter % 1024 == 0)
{
BlockCipher base = cfbEngine.getUnderlyingCipher();
@@ -87,18 +98,18 @@ public class GCFBBlockCipher
key = new KeyParameter(nextKey);
- byte[] iv = new byte[8];
-
base.init(true, key);
- base.processBlock(cfbEngine.getCurrentIV(), 0, iv, 0);
+ byte[] iv = cfbEngine.getCurrentIV();
+
+ base.processBlock(iv, 0, iv, 0);
cfbEngine.init(forEncryption, new ParametersWithIV(key, iv));
}
- counter += cfbEngine.getBlockSize();
+ counter++;
- return cfbEngine.processBlock(in, inOff, out, outOff);
+ return cfbEngine.calculateByte(b);
}
public void reset()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
index 9e617ec9..93f0fe92 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -4,15 +4,17 @@ import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.modes.gcm.GCMExponentiator;
import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.GCMUtil;
import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
/**
* Implements the Galois/Counter mode (GCM) detailed in
@@ -23,7 +25,7 @@ public class GCMBlockCipher
{
private static final int BLOCK_SIZE = 16;
- // not final due to a compiler bug
+ // not final due to a compiler bug
private BlockCipher cipher;
private GCMMultiplier multiplier;
private GCMExponentiator exp;
@@ -81,6 +83,10 @@ public class GCMBlockCipher
return cipher.getAlgorithmName() + "/GCM";
}
+ /**
+ * NOTE: MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits.
+ * Sizes less than 96 are not recommended, but are supported for specialized applications.
+ */
public void init(boolean forEncryption, CipherParameters params)
throws IllegalArgumentException
{
@@ -97,12 +103,12 @@ public class GCMBlockCipher
initialAssociatedText = param.getAssociatedText();
int macSizeBits = param.getMacSize();
- if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0)
+ if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0)
{
throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits);
}
- macSize = macSizeBits / 8;
+ macSize = macSizeBits / 8;
keyParam = param.getKey();
}
else if (params instanceof ParametersWithIV)
@@ -119,7 +125,7 @@ public class GCMBlockCipher
throw new IllegalArgumentException("invalid parameters passed to GCM");
}
- int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize);
+ int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize);
this.bufBlock = new byte[bufLength];
if (nonce == null || nonce.length < 1)
@@ -127,9 +133,7 @@ public class GCMBlockCipher
throw new IllegalArgumentException("IV must be at least 1 byte");
}
- // TODO This should be configurable by init parameters
- // (but must be 16 if nonce length not 12) (BLOCK_SIZE?)
-// this.tagLength = 16;
+ // TODO Restrict macSize to 16 if nonce length not 12?
// Cipher always used in forward mode
// if keyParam is null we're reusing the last key.
@@ -144,6 +148,10 @@ public class GCMBlockCipher
multiplier.init(H);
exp = null;
}
+ else if (this.H == null)
+ {
+ throw new IllegalArgumentException("Key must be specified in initial init");
+ }
this.J0 = new byte[BLOCK_SIZE];
@@ -188,7 +196,7 @@ public class GCMBlockCipher
if (forEncryption)
{
- return totalData + macSize;
+ return totalData + macSize;
}
return totalData < macSize ? 0 : totalData - macSize;
@@ -271,6 +279,10 @@ public class GCMBlockCipher
public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
throws DataLengthException
{
+ if (in.length < (inOff + len))
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
int resultLen = 0;
for (int i = 0; i < len; ++i)
@@ -288,6 +300,10 @@ public class GCMBlockCipher
private void outputBlock(byte[] output, int offset)
{
+ if (output.length < (offset + BLOCK_SIZE))
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
if (totalLength == 0)
{
initCipher();
@@ -324,6 +340,10 @@ public class GCMBlockCipher
if (extra > 0)
{
+ if (out.length < (outOff + extra))
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
gCTRPartial(bufBlock, 0, extra, out, outOff);
}
@@ -347,7 +367,7 @@ public class GCMBlockCipher
// Find the difference between the AAD hashes
if (atLengthPre > 0)
{
- xor(S_at, S_atPre);
+ GCMUtil.xor(S_at, S_atPre);
}
// Number of cipher-text blocks produced
@@ -363,10 +383,10 @@ public class GCMBlockCipher
exp.exponentiateX(c, H_c);
// Carry the difference forward
- multiply(S_at, H_c);
+ GCMUtil.multiply(S_at, H_c);
// Adjust the current hash
- xor(S, S_at);
+ GCMUtil.xor(S, S_at);
}
// Final gHASH
@@ -376,11 +396,10 @@ public class GCMBlockCipher
gHASHBlock(S, X);
- // TODO Fix this if tagLength becomes configurable
// T = MSBt(GCTRk(J0,S))
byte[] tag = new byte[BLOCK_SIZE];
cipher.processBlock(J0, 0, tag, 0);
- xor(tag, S);
+ GCMUtil.xor(tag, S);
int resultLen = extra;
@@ -390,6 +409,10 @@ public class GCMBlockCipher
if (forEncryption)
{
+ if (out.length < (outOff + extra + macSize))
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
// Append T to the message
System.arraycopy(macBlock, 0, out, outOff + bufOff, macSize);
resultLen += macSize;
@@ -451,7 +474,7 @@ public class GCMBlockCipher
{
byte[] tmp = getNextCounterBlock();
- xor(tmp, block);
+ GCMUtil.xor(tmp, block);
System.arraycopy(tmp, 0, out, outOff, BLOCK_SIZE);
gHASHBlock(S, forEncryption ? tmp : block);
@@ -463,7 +486,7 @@ public class GCMBlockCipher
{
byte[] tmp = getNextCounterBlock();
- xor(tmp, buf, off, len);
+ GCMUtil.xor(tmp, buf, off, len);
System.arraycopy(tmp, 0, out, outOff, len);
gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
@@ -482,13 +505,13 @@ public class GCMBlockCipher
private void gHASHBlock(byte[] Y, byte[] b)
{
- xor(Y, b);
+ GCMUtil.xor(Y, b);
multiplier.multiplyH(Y);
}
private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
{
- xor(Y, b, off, len);
+ GCMUtil.xor(Y, b, off, len);
multiplier.multiplyH(Y);
}
@@ -510,65 +533,4 @@ public class GCMBlockCipher
cipher.processBlock(counter, 0, tmp, 0);
return tmp;
}
-
- private static void multiply(byte[] block, byte[] val)
- {
- byte[] tmp = Arrays.clone(block);
- byte[] c = new byte[16];
-
- for (int i = 0; i < 16; ++i)
- {
- byte bits = val[i];
- for (int j = 7; j >= 0; --j)
- {
- if ((bits & (1 << j)) != 0)
- {
- xor(c, tmp);
- }
-
- boolean lsb = (tmp[15] & 1) != 0;
- shiftRight(tmp);
- if (lsb)
- {
- // R = new byte[]{ 0xe1, ... };
-// xor(v, R);
- tmp[0] ^= (byte)0xe1;
- }
- }
- }
-
- System.arraycopy(c, 0, block, 0, 16);
- }
-
- private static void shiftRight(byte[] block)
- {
- int i = 0;
- int bit = 0;
- for (;;)
- {
- int b = block[i] & 0xff;
- block[i] = (byte) ((b >>> 1) | bit);
- if (++i == 16)
- {
- break;
- }
- bit = (b & 1) << 7;
- }
- }
-
- private static void xor(byte[] block, byte[] val)
- {
- for (int i = 15; i >= 0; --i)
- {
- block[i] ^= val[i];
- }
- }
-
- private static void xor(byte[] block, byte[] val, int off, int len)
- {
- while (len-- > 0)
- {
- block[len] ^= val[off + len];
- }
- }
}
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 0e66cf3f..b025a1b8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
@@ -3,17 +3,19 @@ package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* implements the GOST 28147 OFB counter mode (GCTR).
*/
public class GOFBBlockCipher
- implements BlockCipher
+ extends StreamBlockCipher
{
private byte[] IV;
private byte[] ofbV;
private byte[] ofbOutV;
+ private int byteCount;
private final int blockSize;
private final BlockCipher cipher;
@@ -34,6 +36,8 @@ public class GOFBBlockCipher
public GOFBBlockCipher(
BlockCipher cipher)
{
+ super(cipher);
+
this.cipher = cipher;
this.blockSize = cipher.getBlockSize();
@@ -48,16 +52,6 @@ public class GOFBBlockCipher
}
/**
- * return the underlying block cipher that we are wrapping.
- *
- * @return the underlying block cipher that we are wrapping.
- */
- public BlockCipher getUnderlyingCipher()
- {
- return cipher;
- }
-
- /**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
* An IV which is too short is handled in FIPS compliant fashion.
@@ -127,7 +121,6 @@ public class GOFBBlockCipher
return cipher.getAlgorithmName() + "/GCTR";
}
-
/**
* return the block size we are operating at (in bytes).
*
@@ -158,44 +151,7 @@ public class GOFBBlockCipher
int outOff)
throws DataLengthException, IllegalStateException
{
- if ((inOff + blockSize) > in.length)
- {
- throw new DataLengthException("input buffer too short");
- }
-
- if ((outOff + blockSize) > out.length)
- {
- throw new DataLengthException("output buffer too short");
- }
-
- if (firstStep)
- {
- firstStep = false;
- cipher.processBlock(ofbV, 0, ofbOutV, 0);
- N3 = bytesToint(ofbOutV, 0);
- N4 = bytesToint(ofbOutV, 4);
- }
- N3 += C2;
- N4 += C1;
- intTobytes(N3, ofbV, 0);
- intTobytes(N4, ofbV, 4);
-
- cipher.processBlock(ofbV, 0, ofbOutV, 0);
-
- //
- // XOR the ofbV with the plaintext producing the cipher text (and
- // the next input block).
- //
- for (int i = 0; i < blockSize; i++)
- {
- out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]);
- }
-
- //
- // change over the input block.
- //
- System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
- System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
+ processBytes(in, inOff, blockSize, out, outOff);
return blockSize;
}
@@ -210,7 +166,7 @@ public class GOFBBlockCipher
N3 = 0;
N4 = 0;
System.arraycopy(IV, 0, ofbV, 0, IV.length);
-
+ byteCount = 0;
cipher.reset();
}
@@ -234,4 +190,39 @@ public class GOFBBlockCipher
out[outOff + 1] = (byte)(num >>> 8);
out[outOff] = (byte)num;
}
+
+ protected byte calculateByte(byte b)
+ {
+ if (byteCount == 0)
+ {
+ if (firstStep)
+ {
+ firstStep = false;
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+ N3 = bytesToint(ofbOutV, 0);
+ N4 = bytesToint(ofbOutV, 4);
+ }
+ N3 += C2;
+ N4 += C1;
+ intTobytes(N3, ofbV, 0);
+ intTobytes(N4, ofbV, 4);
+
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+ }
+
+ byte rv = (byte)(ofbOutV[byteCount++] ^ b);
+
+ if (byteCount == blockSize)
+ {
+ byteCount = 0;
+
+ //
+ // change over the input block.
+ //
+ System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
+ System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
+ }
+
+ return rv;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java
new file mode 100644
index 00000000..fe7bf971
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java
@@ -0,0 +1,337 @@
+/**
+ * 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.
+ */
+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;
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * 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 class implements the NIST version as documented in "Addendum to NIST SP 800-38A, Recommendation for Block Cipher Modes of Operation: Three Variants of Ciphertext Stealing for CBC Mode"
+ * </p>
+ */
+public class NISTCTSBlockCipher
+ extends BufferedBlockCipher
+{
+ public static final int CS1 = 1;
+ public static final int CS2 = 2;
+ public static final int CS3 = 3;
+
+ private final int type;
+ private final int blockSize;
+
+ /**
+ * Create a buffered block cipher that uses NIST Cipher Text Stealing
+ *
+ * @param type type of CTS mode (CS1, CS2, or CS3)
+ * @param cipher the underlying block cipher used to create the CBC block cipher this cipher uses..
+ */
+ public NISTCTSBlockCipher(
+ int type,
+ BlockCipher cipher)
+ {
+ this.type = type;
+ this.cipher = new CBCBlockCipher(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)
+ {
+ if (bufOff < blockSize)
+ {
+ throw new DataLengthException("need at least one block of input for NISTCTS");
+ }
+
+ if (bufOff > blockSize)
+ {
+ byte[] lastBlock = new byte[blockSize];
+
+ if (this.type == CS2 || this.type == CS3)
+ {
+ cipher.processBlock(buf, 0, block, 0);
+
+ System.arraycopy(buf, blockSize, lastBlock, 0, len);
+
+ cipher.processBlock(lastBlock, 0, lastBlock, 0);
+
+ if (this.type == CS2 && len == blockSize)
+ {
+ System.arraycopy(block, 0, out, outOff, blockSize);
+
+ System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
+ }
+ else
+ {
+ System.arraycopy(lastBlock, 0, out, outOff, blockSize);
+
+ System.arraycopy(block, 0, out, outOff + blockSize, len);
+ }
+ }
+ else
+ {
+ System.arraycopy(buf, 0, block, 0, blockSize);
+ cipher.processBlock(block, 0, block, 0);
+ System.arraycopy(block, 0, out, outOff, len);
+
+ System.arraycopy(buf, bufOff - len, lastBlock, 0, len);
+ cipher.processBlock(lastBlock, 0, lastBlock, 0);
+ System.arraycopy(lastBlock, 0, out, outOff + len, blockSize);
+ }
+ }
+ else
+ {
+ cipher.processBlock(buf, 0, block, 0);
+
+ System.arraycopy(block, 0, out, outOff, blockSize);
+ }
+ }
+ else
+ {
+ if (bufOff < blockSize)
+ {
+ throw new DataLengthException("need at least one block of input for CTS");
+ }
+
+ byte[] lastBlock = new byte[blockSize];
+
+ if (bufOff > blockSize)
+ {
+ if (this.type == CS3 || (this.type == CS2 && ((buf.length - bufOff) % blockSize) != 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
+ {
+ BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+ c.processBlock(buf, bufOff - blockSize, lastBlock, 0);
+
+ System.arraycopy(buf, 0, block, 0, blockSize);
+
+ if (len != blockSize)
+ {
+ System.arraycopy(lastBlock, len, block, len, blockSize - len);
+ }
+
+ cipher.processBlock(block, 0, block, 0);
+
+ System.arraycopy(block, 0, out, outOff, blockSize);
+
+ for (int i = 0; i != len; i++)
+ {
+ lastBlock[i] ^= buf[i];
+ }
+
+ System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
+ }
+ }
+ else
+ {
+ cipher.processBlock(buf, 0, block, 0);
+
+ System.arraycopy(block, 0, out, outOff, blockSize);
+ }
+ }
+
+ int offset = bufOff;
+
+ reset();
+
+ return offset;
+ }
+}
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 6dc71481..86263914 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
@@ -6,29 +6,28 @@ import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
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-03">The OCB Authenticated-Encryption
- * Algorithm</a>, licensed per:
- * <p/>
+ * An implementation of <a href="http://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB
+ * Authenticated-Encryption Algorithm</a>, licensed per:
+ * <p>
* <blockquote> <a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for
* Open-Source Software Implementations of OCB</a> (Jan 9, 2013) &mdash; &ldquo;License 1&rdquo; <br>
* Under this license, you are authorized to make, use, and distribute open-source software
* implementations of OCB. This license terminates for you if you sue someone over their open-source
* software implementation of OCB claiming that you have a patent covering their implementation.
- * <p/>
+ * <p>
* This is a non-binding summary of a legal document (the link above). The parameters of the license
* are specified in the license document and that document is controlling. </blockquote>
*/
public class OCBBlockCipher
implements AEADBlockCipher
{
-
private static final int BLOCK_SIZE = 16;
private BlockCipher hashCipher;
@@ -51,7 +50,9 @@ public class OCBBlockCipher
/*
* NONCE-DEPENDENT
*/
- private byte[] OffsetMAIN_0;
+ private byte[] KtopInput = null;
+ private byte[] Stretch = new byte[24];
+ private byte[] OffsetMAIN_0 = new byte[16];
/*
* PER-ENCRYPTION/DECRYPTION
@@ -61,7 +62,7 @@ public class OCBBlockCipher
private long hashBlockCount, mainBlockCount;
private byte[] OffsetHASH;
private byte[] Sum;
- private byte[] OffsetMAIN;
+ private byte[] OffsetMAIN = new byte[16];
private byte[] Checksum;
// NOTE: The MAC value is preserved after doFinal
@@ -111,6 +112,7 @@ public class OCBBlockCipher
public void init(boolean forEncryption, CipherParameters parameters)
throws IllegalArgumentException
{
+ boolean oldForEncryption = this.forEncryption;
this.forEncryption = forEncryption;
this.macBlock = null;
@@ -164,14 +166,17 @@ public class OCBBlockCipher
* KEY-DEPENDENT INITIALISATION
*/
- if (keyParameter == null)
+ if (keyParameter != null)
{
- // TODO If 'keyParameter' is null we're re-using the last key.
+ // hashCipher always used in forward mode
+ hashCipher.init(true, keyParameter);
+ mainCipher.init(forEncryption, keyParameter);
+ KtopInput = null;
+ }
+ else if (oldForEncryption != forEncryption)
+ {
+ throw new IllegalArgumentException("cannot change encrypting state without providing key.");
}
-
- // hashCipher always used in forward mode
- hashCipher.init(true, keyParameter);
- mainCipher.init(forEncryption, keyParameter);
this.L_Asterisk = new byte[16];
hashCipher.processBlock(L_Asterisk, 0, L_Asterisk, 0);
@@ -185,25 +190,8 @@ public class OCBBlockCipher
* NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALISATION
*/
- byte[] nonce = new byte[16];
- System.arraycopy(N, 0, nonce, nonce.length - N.length, N.length);
- nonce[0] = (byte)(macSize << 4);
- nonce[15 - N.length] |= 1;
+ int bottom = processNonce(N);
- int bottom = nonce[15] & 0x3F;
-
- byte[] Ktop = new byte[16];
- nonce[15] &= 0xC0;
- hashCipher.processBlock(nonce, 0, Ktop, 0);
-
- byte[] Stretch = new byte[24];
- System.arraycopy(Ktop, 0, Stretch, 0, 16);
- for (int i = 0; i < 8; ++i)
- {
- Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]);
- }
-
- this.OffsetMAIN_0 = new byte[16];
int bits = bottom % 8, bytes = bottom / 8;
if (bits == 0)
{
@@ -227,7 +215,7 @@ public class OCBBlockCipher
this.OffsetHASH = new byte[16];
this.Sum = new byte[16];
- this.OffsetMAIN = Arrays.clone(this.OffsetMAIN_0);
+ System.arraycopy(this.OffsetMAIN_0, 0, this.OffsetMAIN, 0, 16);
this.Checksum = new byte[16];
if (initialAssociatedText != null)
@@ -236,6 +224,34 @@ public class OCBBlockCipher
}
}
+ protected int processNonce(byte[] N)
+ {
+ byte[] nonce = new byte[16];
+ System.arraycopy(N, 0, nonce, nonce.length - N.length, N.length);
+ nonce[0] = (byte)(macSize << 4);
+ nonce[15 - N.length] |= 1;
+
+ int bottom = nonce[15] & 0x3F;
+ nonce[15] &= 0xC0;
+
+ /*
+ * When used with incrementing nonces, the cipher is only applied once every 64 inits.
+ */
+ if (KtopInput == null || !Arrays.areEqual(nonce, KtopInput))
+ {
+ byte[] Ktop = new byte[16];
+ KtopInput = nonce;
+ hashCipher.processBlock(KtopInput, 0, Ktop, 0);
+ System.arraycopy(Ktop, 0, Stretch, 0, 16);
+ for (int i = 0; i < 8; ++i)
+ {
+ Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]);
+ }
+ }
+
+ return bottom;
+ }
+
public byte[] getMac()
{
return Arrays.clone(macBlock);
@@ -301,6 +317,10 @@ public class OCBBlockCipher
public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
throws DataLengthException
{
+ if (input.length < (inOff + len))
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
int resultLen = 0;
for (int i = 0; i < len; ++i)
@@ -362,6 +382,10 @@ public class OCBBlockCipher
xor(mainBlock, Pad);
+ if (output.length < (outOff + mainBlockPos))
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
System.arraycopy(mainBlock, 0, output, outOff, mainBlockPos);
if (!forEncryption)
@@ -389,6 +413,10 @@ public class OCBBlockCipher
if (forEncryption)
{
+ if (output.length < (outOff + resultLen + macSize))
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
// Append tag to the message
System.arraycopy(macBlock, 0, output, outOff + resultLen, macSize);
resultLen += macSize;
@@ -440,6 +468,11 @@ public class OCBBlockCipher
protected void processMainBlock(byte[] output, int outOff)
{
+ if (output.length < (outOff + BLOCK_SIZE))
+ {
+ throw new OutputLengthException("Output buffer too short");
+ }
+
/*
* OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
*/
@@ -537,7 +570,7 @@ public class OCBBlockCipher
while ((x & 1L) == 0L)
{
++n;
- x >>= 1;
+ x >>>= 1;
}
return n;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
index 5297698f..d9ff428f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
@@ -3,14 +3,16 @@ package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* implements a Output-FeedBack (OFB) mode on top of a simple cipher.
*/
public class OFBBlockCipher
- implements BlockCipher
+ extends StreamBlockCipher
{
+ private int byteCount;
private byte[] IV;
private byte[] ofbV;
private byte[] ofbOutV;
@@ -29,6 +31,8 @@ public class OFBBlockCipher
BlockCipher cipher,
int blockSize)
{
+ super(cipher);
+
this.cipher = cipher;
this.blockSize = blockSize / 8;
@@ -38,16 +42,6 @@ public class OFBBlockCipher
}
/**
- * return the underlying block cipher that we are wrapping.
- *
- * @return the underlying block cipher that we are wrapping.
- */
- public BlockCipher getUnderlyingCipher()
- {
- return cipher;
- }
-
- /**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
* An IV which is too short is handled in FIPS compliant fashion.
@@ -113,7 +107,7 @@ public class OFBBlockCipher
return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8);
}
-
+
/**
* return the block size we are operating at (in bytes).
*
@@ -144,32 +138,7 @@ public class OFBBlockCipher
int outOff)
throws DataLengthException, IllegalStateException
{
- if ((inOff + blockSize) > in.length)
- {
- throw new DataLengthException("input buffer too short");
- }
-
- if ((outOff + blockSize) > out.length)
- {
- throw new DataLengthException("output buffer too short");
- }
-
- cipher.processBlock(ofbV, 0, ofbOutV, 0);
-
- //
- // XOR the ofbV with the plaintext producing the cipher text (and
- // the next input block).
- //
- for (int i = 0; i < blockSize; i++)
- {
- out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]);
- }
-
- //
- // change over the input block.
- //
- System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
- System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
+ processBytes(in, inOff, blockSize, out, outOff);
return blockSize;
}
@@ -181,7 +150,29 @@ public class OFBBlockCipher
public void reset()
{
System.arraycopy(IV, 0, ofbV, 0, IV.length);
+ byteCount = 0;
cipher.reset();
}
+
+ protected byte calculateByte(byte in)
+ throws DataLengthException, IllegalStateException
+ {
+ if (byteCount == 0)
+ {
+ cipher.processBlock(ofbV, 0, ofbOutV, 0);
+ }
+
+ byte rv = (byte)(ofbOutV[byteCount++] ^ in);
+
+ if (byteCount == blockSize)
+ {
+ byteCount = 0;
+
+ System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
+ System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
+ }
+
+ return rv;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
index da8c4ae1..fbc8bf45 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
@@ -3,14 +3,18 @@ package org.bouncycastle.crypto.modes;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.SkippingStreamCipher;
+import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Pack;
/**
* Implements the Segmented Integer Counter (SIC) mode on top of a simple
* block cipher. This mode is also known as CTR mode.
*/
public class SICBlockCipher
- implements BlockCipher
+ extends StreamBlockCipher
+ implements SkippingStreamCipher
{
private final BlockCipher cipher;
private final int blockSize;
@@ -18,7 +22,7 @@ public class SICBlockCipher
private byte[] IV;
private byte[] counter;
private byte[] counterOut;
-
+ private int byteCount;
/**
* Basic constructor.
@@ -27,25 +31,16 @@ public class SICBlockCipher
*/
public SICBlockCipher(BlockCipher c)
{
+ super(c);
+
this.cipher = c;
this.blockSize = cipher.getBlockSize();
this.IV = new byte[blockSize];
this.counter = new byte[blockSize];
this.counterOut = new byte[blockSize];
+ this.byteCount = 0;
}
-
- /**
- * return the underlying block cipher that we are wrapping.
- *
- * @return the underlying block cipher that we are wrapping.
- */
- public BlockCipher getUnderlyingCipher()
- {
- return cipher;
- }
-
-
public void init(
boolean forEncryption, //ignored by this CTR mode
CipherParameters params)
@@ -53,17 +48,17 @@ public class SICBlockCipher
{
if (params instanceof ParametersWithIV)
{
- ParametersWithIV ivParam = (ParametersWithIV)params;
- byte[] iv = ivParam.getIV();
- System.arraycopy(iv, 0, IV, 0, IV.length);
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+ System.arraycopy(iv, 0, IV, 0, IV.length);
- reset();
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
- // if null it's an IV changed only.
- if (ivParam.getParameters() != null)
- {
- cipher.init(true, ivParam.getParameters());
- }
+ reset();
}
else
{
@@ -81,33 +76,240 @@ public class SICBlockCipher
return cipher.getBlockSize();
}
-
public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
- cipher.processBlock(counter, 0, counterOut, 0);
+ processBytes(in, inOff, blockSize, out, outOff);
- //
- // XOR the counterOut with the plaintext producing the cipher text
- //
- for (int i = 0; i < counterOut.length; i++)
+ return blockSize;
+ }
+
+ protected byte calculateByte(byte in)
+ throws DataLengthException, IllegalStateException
+ {
+ if (byteCount == 0)
+ {
+ cipher.processBlock(counter, 0, counterOut, 0);
+
+ return (byte)(counterOut[byteCount++] ^ in);
+ }
+
+ byte rv = (byte)(counterOut[byteCount++] ^ in);
+
+ if (byteCount == counter.length)
{
- out[outOff + i] = (byte)(counterOut[i] ^ in[inOff + i]);
+ byteCount = 0;
+
+ incrementCounter();
}
+ return rv;
+ }
+
+ private void incrementCounterPow2(int pow2Div8)
+ {
+ // increment counter by 1 << 8 * pow2Div8
+ for (int i = counter.length - (1 + pow2Div8); i >= 0 && ++counter[i] == 0; i--)
+ {
+ ; // do nothing - pre-increment and test for 0 in counter does the job.
+ }
+ }
+
+ private void incrementCounter(int offSet)
+ {
+ byte old = counter[counter.length - 1];
+
+ counter[counter.length - 1] += offSet;
+
+ if (old != 0 && counter[counter.length - 1] < old)
+ {
+ incrementCounterPow2(1);
+ }
+ }
+
+ private void incrementCounter()
+ {
// increment counter by 1.
for (int i = counter.length - 1; i >= 0 && ++counter[i] == 0; i--)
{
; // do nothing - pre-increment and test for 0 in counter does the job.
}
+ }
+
+ private void decrementCounterPow2(int pow2Div8)
+ {
+ if (counter[pow2Div8] == 0)
+ {
+ boolean nonZero = false;
+
+ for (int i = counter.length - (1 + pow2Div8); i > 0; i--)
+ {
+ if (counter[i] != 0)
+ {
+ nonZero = true;
+ }
+ }
+
+ if (!nonZero)
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+ }
- return counter.length;
+ // decrement counter by 1.
+ for (int i = counter.length - (1 + pow2Div8); i >= 0 && --counter[i] == -1; i--)
+ {
+ ;
+ }
}
+ private void decrementCounter()
+ {
+ if (counter[0] == 0)
+ {
+ boolean nonZero = false;
+
+ for (int i = counter.length - 1; i > 0; i--)
+ {
+ if (counter[i] != 0)
+ {
+ nonZero = true;
+ }
+ }
+
+ if (!nonZero)
+ {
+ throw new IllegalStateException("attempt to reduce counter past zero.");
+ }
+ }
+
+ // decrement counter by 1.
+ for (int i = counter.length - 1; i >= 0 && --counter[i] == -1; i--)
+ {
+ ;
+ }
+ }
+
+ private void adjustCounter(long n)
+ {
+ if (n >= 0)
+ {
+ long numBlocks = (n + byteCount) / blockSize;
+
+ if (numBlocks > 255)
+ {
+ long gap = numBlocks;
+
+ for (int i = 5; i >= 1; i--)
+ {
+ long diff = 1L << (8 * i);
+
+ while (gap >= diff)
+ {
+ incrementCounterPow2(i);
+
+ gap -= diff;
+ }
+ }
+
+ incrementCounter((int)gap);
+ }
+ else
+ {
+ incrementCounter((int)numBlocks);
+ }
+
+ byteCount = (int)((n + byteCount) - (blockSize * numBlocks));
+ }
+ else
+ {
+ long numBlocks = (-n - byteCount) / blockSize;
+
+ if (numBlocks > 255)
+ {
+ long gap = numBlocks;
+
+ for (int i = 5; i >= 1; i--)
+ {
+ long diff = 1L << (8 * i);
+
+ while (gap > diff)
+ {
+ decrementCounterPow2(i);
+
+ gap -= diff;
+ }
+ }
+
+ for (long i = 0; i != gap; i++)
+ {
+ decrementCounter();
+ }
+ }
+ else
+ {
+ for (long i = 0; i != numBlocks; i++)
+ {
+ decrementCounter();
+ }
+ }
+
+ int gap = (int)(byteCount + n + (blockSize * numBlocks));
+
+ if (gap >= 0)
+ {
+ byteCount = 0;
+ }
+ else
+ {
+ decrementCounter();
+ byteCount = blockSize + gap;
+ }
+ }
+ }
public void reset()
{
System.arraycopy(IV, 0, counter, 0, counter.length);
cipher.reset();
+ this.byteCount = 0;
+ }
+
+ public long skip(long numberOfBytes)
+ {
+ adjustCounter(numberOfBytes);
+
+ cipher.processBlock(counter, 0, counterOut, 0);
+
+ return numberOfBytes;
+ }
+
+ public long seekTo(long position)
+ {
+ reset();
+
+ return skip(position);
+ }
+
+ public long getPosition()
+ {
+ byte[] res = new byte[IV.length];
+
+ System.arraycopy(counter, 0, res, 0, res.length);
+
+ for (int i = res.length - 1; i >= 1; i--)
+ {
+ int v = (res[i] & 0xff) - (IV[i] & 0xff);
+
+ if (v < 0)
+ {
+ res[i - 1]--;
+ v += 256;
+ }
+
+ res[i] = (byte)v;
+ }
+
+ return Pack.bigEndianToLong(res, res.length - 8) * blockSize + byteCount;
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java
index a98d5b2a..2afb18fc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMMultiplier.java
@@ -1,18 +1,18 @@
package org.bouncycastle.crypto.modes.gcm;
-import org.bouncycastle.util.Arrays;
-
public class BasicGCMMultiplier implements GCMMultiplier
{
- private byte[] H;
+ private int[] H;
public void init(byte[] H)
{
- this.H = Arrays.clone(H);
+ this.H = GCMUtil.asInts(H);
}
public void multiplyH(byte[] x)
{
- GCMUtil.multiply(x, H);
+ int[] t = GCMUtil.asInts(x);
+ GCMUtil.multiply(t, H);
+ GCMUtil.asBytes(t, x);
}
}
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 3031a444..58f40788 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
@@ -1,13 +1,11 @@
package org.bouncycastle.crypto.modes.gcm;
-import org.bouncycastle.crypto.util.Pack;
-import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
-abstract class GCMUtil
+public 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 final long E1L = (E1 & 0xFFFFFFFFL) << 32;
private static int[] generateLookup()
{
@@ -31,170 +29,151 @@ abstract class GCMUtil
private static final int[] LOOKUP = generateLookup();
- static byte[] oneAsBytes()
+ public static byte[] oneAsBytes()
{
byte[] tmp = new byte[16];
tmp[0] = (byte)0x80;
return tmp;
}
- static int[] oneAsInts()
+ public static int[] oneAsInts()
{
int[] tmp = new int[4];
tmp[0] = 1 << 31;
return tmp;
}
- static long[] oneAsLongs()
+ public static long[] oneAsLongs()
{
long[] tmp = new long[2];
tmp[0] = 1L << 63;
return tmp;
}
- static byte[] asBytes(int[] x)
+ public static byte[] asBytes(int[] x)
{
byte[] z = new byte[16];
Pack.intToBigEndian(x, z, 0);
return z;
}
- static void asBytes(int[] x, byte[] z)
+ public static void asBytes(int[] x, byte[] z)
{
Pack.intToBigEndian(x, z, 0);
}
- static byte[] asBytes(long[] x)
+ public static byte[] asBytes(long[] x)
{
byte[] z = new byte[16];
Pack.longToBigEndian(x, z, 0);
return z;
}
- static void asBytes(long[] x, byte[] z)
+ public static void asBytes(long[] x, byte[] z)
{
Pack.longToBigEndian(x, z, 0);
}
- static int[] asInts(byte[] x)
+ public static int[] asInts(byte[] x)
{
int[] z = new int[4];
Pack.bigEndianToInt(x, 0, z);
return z;
}
- static void asInts(byte[] x, int[] z)
+ public static void asInts(byte[] x, int[] z)
{
Pack.bigEndianToInt(x, 0, z);
}
- static long[] asLongs(byte[] x)
+ public static long[] asLongs(byte[] x)
{
long[] z = new long[2];
Pack.bigEndianToLong(x, 0, z);
return z;
}
- static void asLongs(byte[] x, long[] z)
+ public static void asLongs(byte[] x, long[] z)
{
Pack.bigEndianToLong(x, 0, z);
}
- static void multiply(byte[] x, byte[] y)
+ public static void multiply(byte[] x, byte[] y)
{
- byte[] r0 = Arrays.clone(x);
- byte[] r1 = new byte[16];
-
- for (int i = 0; i < 16; ++i)
- {
- byte bits = y[i];
- for (int j = 7; j >= 0; --j)
- {
- if ((bits & (1 << j)) != 0)
- {
- xor(r1, r0);
- }
-
- if (shiftRight(r0) != 0)
- {
- r0[0] ^= E1B;
- }
- }
- }
-
- System.arraycopy(r1, 0, x, 0, 16);
+ int[] t1 = GCMUtil.asInts(x);
+ int[] t2 = GCMUtil.asInts(y);
+ GCMUtil.multiply(t1, t2);
+ GCMUtil.asBytes(t1, x);
}
- static void multiply(int[] x, int[] y)
+ public static void multiply(int[] x, int[] y)
{
- int[] r0 = Arrays.clone(x);
- int[] r1 = new int[4];
-
+ int r00 = x[0], r01 = x[1], r02 = x[2], r03 = x[3];
+ int r10 = 0, r11 = 0, r12 = 0, r13 = 0;
+
for (int i = 0; i < 4; ++i)
{
int bits = y[i];
- for (int j = 31; j >= 0; --j)
+ for (int j = 0; j < 32; ++j)
{
- if ((bits & (1 << j)) != 0)
- {
- xor(r1, r0);
- }
-
- if (shiftRight(r0) != 0)
- {
- r0[0] ^= E1;
- }
+ int m1 = bits >> 31; bits <<= 1;
+ r10 ^= (r00 & m1);
+ r11 ^= (r01 & m1);
+ r12 ^= (r02 & m1);
+ r13 ^= (r03 & m1);
+
+ int m2 = (r03 << 31) >> 8;
+ r03 = (r03 >>> 1) | (r02 << 63);
+ r02 = (r02 >>> 1) | (r01 << 63);
+ r01 = (r01 >>> 1) | (r00 << 63);
+ r00 = (r00 >>> 1) ^ (m2 & E1);
}
}
- System.arraycopy(r1, 0, x, 0, 4);
+ x[0] = r10;
+ x[1] = r11;
+ x[2] = r12;
+ x[3] = r13;
}
- static void multiply(long[] x, long[] y)
+ public static void multiply(long[] x, long[] y)
{
- long[] r0 = new long[]{ x[0], x[1] };
- long[] r1 = new long[2];
+ long r00 = x[0], r01 = x[1], r10 = 0, r11 = 0;
for (int i = 0; i < 2; ++i)
{
long bits = y[i];
- for (int j = 63; j >= 0; --j)
+ for (int j = 0; j < 64; ++j)
{
- if ((bits & (1L << j)) != 0)
- {
- xor(r1, r0);
- }
+ long m1 = bits >> 63; bits <<= 1;
+ r10 ^= (r00 & m1);
+ r11 ^= (r01 & m1);
- if (shiftRight(r0) != 0)
- {
- r0[0] ^= E1L;
- }
+ long m2 = (r01 << 63) >> 8;
+ r01 = (r01 >>> 1) | (r00 << 63);
+ r00 = (r00 >>> 1) ^ (m2 & E1L);
}
}
- x[0] = r1[0];
- x[1] = r1[1];
+ x[0] = r10;
+ x[1] = r11;
}
// P is the value with only bit i=1 set
- static void multiplyP(int[] x)
+ public static void multiplyP(int[] x)
{
- if (shiftRight(x) != 0)
- {
- x[0] ^= E1;
- }
+ int m = shiftRight(x) >> 8;
+ x[0] ^= (m & E1);
}
- static void multiplyP(int[] x, int[] y)
+ public static void multiplyP(int[] x, int[] z)
{
- if (shiftRight(x, y) != 0)
- {
- y[0] ^= E1;
- }
+ int m = shiftRight(x, z) >> 8;
+ z[0] ^= (m & E1);
}
// P is the value with only bit i=1 set
- static void multiplyP8(int[] x)
+ public static void multiplyP8(int[] x)
{
// for (int i = 8; i != 0; --i)
// {
@@ -205,74 +184,12 @@ abstract class GCMUtil
x[0] ^= LOOKUP[c >>> 24];
}
- static void multiplyP8(int[] x, int[] y)
+ public static void multiplyP8(int[] x, int[] y)
{
int c = shiftRightN(x, 8, y);
y[0] ^= LOOKUP[c >>> 24];
}
- static byte shiftRight(byte[] x)
- {
-// 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 = 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 byte shiftRight(byte[] x, byte[] z)
- {
-// 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 = 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 int shiftRight(int[] x)
{
// int c = 0;
@@ -393,7 +310,7 @@ abstract class GCMUtil
return b << nInv;
}
- static void xor(byte[] x, byte[] y)
+ public static void xor(byte[] x, byte[] y)
{
int i = 0;
do
@@ -406,15 +323,15 @@ abstract class GCMUtil
while (i < 16);
}
- static void xor(byte[] x, byte[] y, int yOff, int yLen)
+ public static void xor(byte[] x, byte[] y, int yOff, int yLen)
{
- while (yLen-- > 0)
+ while (--yLen >= 0)
{
x[yLen] ^= y[yOff + yLen];
}
}
- static void xor(byte[] x, byte[] y, byte[] z)
+ public static void xor(byte[] x, byte[] y, byte[] z)
{
int i = 0;
do
@@ -427,7 +344,7 @@ abstract class GCMUtil
while (i < 16);
}
- static void xor(int[] x, int[] y)
+ public static void xor(int[] x, int[] y)
{
x[0] ^= y[0];
x[1] ^= y[1];
@@ -435,7 +352,7 @@ abstract class GCMUtil
x[3] ^= y[3];
}
- static void xor(int[] x, int[] y, int[] z)
+ public static void xor(int[] x, int[] y, int[] z)
{
z[0] = x[0] ^ y[0];
z[1] = x[1] ^ y[1];
@@ -443,13 +360,13 @@ abstract class GCMUtil
z[3] = x[3] ^ y[3];
}
- static void xor(long[] x, long[] y)
+ public static void xor(long[] x, long[] y)
{
x[0] ^= y[0];
x[1] ^= y[1];
}
- static void xor(long[] x, long[] y, long[] z)
+ public 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/Tables64kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
index a34a6ea4..4f32a0d9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables64kGCMMultiplier.java
@@ -1,7 +1,7 @@
package org.bouncycastle.crypto.modes.gcm;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
public class Tables64kGCMMultiplier implements GCMMultiplier
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
index 8535db5a..69c1dce8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -1,7 +1,7 @@
package org.bouncycastle.crypto.modes.gcm;
-import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
public class Tables8kGCMMultiplier implements GCMMultiplier
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/package.html
new file mode 100644
index 00000000..09c42f03
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+GCM mode support classes.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html
new file mode 100644
index 00000000..5402df44
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html
@@ -0,0 +1,5 @@
+<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
new file mode 100644
index 00000000..ee5487ff
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Base classes for the lightweight API.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
index 93b149fa..8b30398f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PKCS7Padding.java
@@ -57,18 +57,19 @@ public class PKCS7Padding
throws InvalidCipherTextException
{
int count = in[in.length - 1] & 0xff;
+ byte countAsbyte = (byte)count;
- if (count > in.length || count == 0)
+ // constant time version
+ boolean failed = (count > in.length | count == 0);
+
+ for (int i = 0; i < in.length; i++)
{
- throw new InvalidCipherTextException("pad block corrupted");
+ failed |= (in.length - i <= count) & (in[i] != countAsbyte);
}
-
- for (int i = 1; i <= count; i++)
+
+ if (failed)
{
- if (in[in.length - i] != count)
- {
- throw new InvalidCipherTextException("pad block corrupted");
- }
+ throw new InvalidCipherTextException("pad block corrupted");
}
return count;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
index ee3fd60e..d5928f70 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java
@@ -125,7 +125,7 @@ public class PaddedBufferedBlockCipher
if (leftOver == 0)
{
- return total - buf.length;
+ return Math.max(0, total - buf.length);
}
return total - leftOver;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html
new file mode 100644
index 00000000..2b82e60f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Paddings for symmetric ciphers.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupKeyGenerationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupKeyGenerationParameters.java
new file mode 100644
index 00000000..001cd599
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupKeyGenerationParameters.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.crypto.params;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+
+public class CramerShoupKeyGenerationParameters
+ extends KeyGenerationParameters
+{
+
+ private CramerShoupParameters params;
+
+ public CramerShoupKeyGenerationParameters(SecureRandom random, CramerShoupParameters params)
+ {
+ super(random, getStrength(params));
+
+ this.params = params;
+ }
+
+ public CramerShoupParameters getParameters()
+ {
+ return params;
+ }
+
+ static int getStrength(CramerShoupParameters params)
+ {
+ return params.getP().bitLength();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupKeyParameters.java
new file mode 100644
index 00000000..9e4219c8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupKeyParameters.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.crypto.params;
+
+public class CramerShoupKeyParameters extends AsymmetricKeyParameter {
+
+ private CramerShoupParameters params;
+
+ protected CramerShoupKeyParameters(boolean isPrivate, CramerShoupParameters params) {
+ super(isPrivate);
+
+ this.params = params;
+ }
+
+ public CramerShoupParameters getParameters() {
+ return params;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CramerShoupKeyParameters)) {
+ return false;
+ }
+
+ CramerShoupKeyParameters csKey = (CramerShoupKeyParameters) obj;
+
+ if (params == null) {
+ return csKey.getParameters() == null;
+ } else {
+ return params.equals(csKey.getParameters());
+ }
+ }
+
+ public int hashCode() {
+ int code = isPrivate() ? 0 : 1;
+
+ if (params != null) {
+ code ^= params.hashCode();
+ }
+
+ return code;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupParameters.java
new file mode 100644
index 00000000..24264b6c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupParameters.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Digest;
+
+public class CramerShoupParameters implements CipherParameters {
+
+ private BigInteger p; // prime order of G
+ private BigInteger g1, g2; // generate G
+
+ private Digest H; // hash function
+
+ public CramerShoupParameters(BigInteger p, BigInteger g1, BigInteger g2, Digest H) {
+ this.p = p;
+ this.g1 = g1;
+ this.g2 = g2;
+ this.H = H;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DSAParameters)) {
+ return false;
+ }
+
+ CramerShoupParameters pm = (CramerShoupParameters) obj;
+
+ return (pm.getP().equals(p) && pm.getG1().equals(g1) && pm.getG2().equals(g2));
+ }
+
+ public int hashCode() {
+ return getP().hashCode() ^ getG1().hashCode() ^ getG2().hashCode();
+ }
+
+ public BigInteger getG1() {
+ return g1;
+ }
+
+ public BigInteger getG2() {
+ return g2;
+ }
+
+ public BigInteger getP() {
+ return p;
+ }
+
+ public Digest getH() {
+ H.reset();
+ return H;
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupPrivateKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupPrivateKeyParameters.java
new file mode 100644
index 00000000..79de46ad
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupPrivateKeyParameters.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class CramerShoupPrivateKeyParameters extends CramerShoupKeyParameters {
+
+ private BigInteger x1, x2, y1, y2, z; // Z_p
+ private CramerShoupPublicKeyParameters pk; // public key
+
+ public CramerShoupPrivateKeyParameters(CramerShoupParameters params, BigInteger x1, BigInteger x2, BigInteger y1, BigInteger y2, BigInteger z) {
+ super(true, params);
+
+ this.x1 = x1;
+ this.x2 = x2;
+ this.y1 = y1;
+ this.y2 = y2;
+ this.z = z;
+ }
+
+ public BigInteger getX1() {
+ return x1;
+ }
+
+ public BigInteger getX2() {
+ return x2;
+ }
+
+ public BigInteger getY1() {
+ return y1;
+ }
+
+ public BigInteger getY2() {
+ return y2;
+ }
+
+ public BigInteger getZ() {
+ return z;
+ }
+
+ public void setPk(CramerShoupPublicKeyParameters pk) {
+ this.pk = pk;
+ }
+
+ public CramerShoupPublicKeyParameters getPk() {
+ return pk;
+ }
+
+ public int hashCode() {
+ return x1.hashCode() ^ x2.hashCode() ^ y1.hashCode() ^ y2.hashCode() ^ z.hashCode() ^ super.hashCode();
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CramerShoupPrivateKeyParameters)) {
+ return false;
+ }
+
+ CramerShoupPrivateKeyParameters other = (CramerShoupPrivateKeyParameters) obj;
+
+ return other.getX1().equals(this.x1) && other.getX2().equals(this.x2) && other.getY1().equals(this.y1) && other.getY2().equals(this.y2) && other.getZ().equals(this.z) && super.equals(obj);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupPublicKeyParameters.java
new file mode 100644
index 00000000..341149c1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/CramerShoupPublicKeyParameters.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class CramerShoupPublicKeyParameters extends CramerShoupKeyParameters {
+
+ private BigInteger c, d, h; // public key group elements
+
+ public CramerShoupPublicKeyParameters(CramerShoupParameters params, BigInteger c, BigInteger d, BigInteger h) {
+ super(false, params);
+
+ this.c = c;
+ this.d = d;
+ this.h = h;
+ }
+
+ public BigInteger getC() {
+ return c;
+ }
+
+ public BigInteger getD() {
+ return d;
+ }
+
+ public BigInteger getH() {
+ return h;
+ }
+
+ public int hashCode() {
+ return c.hashCode() ^ d.hashCode() ^ h.hashCode() ^ super.hashCode();
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CramerShoupPublicKeyParameters)) {
+ return false;
+ }
+
+ CramerShoupPublicKeyParameters other = (CramerShoupPublicKeyParameters) obj;
+
+ return other.getC().equals(c) && other.getD().equals(d) && other.getH().equals(h) && super.equals(obj);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
index b679287c..fec6dfdc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/DHParameters.java
@@ -84,8 +84,7 @@ public class DHParameters
{
if (l != 0)
{
- BigInteger bigL = BigInteger.valueOf(2L ^ (l - 1));
- if (bigL.compareTo(p) == 1)
+ if (l > p.bitLength())
{
throw new IllegalArgumentException("when l value specified, it must satisfy 2^(l-1) <= p");
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECNamedDomainParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECNamedDomainParameters.java
new file mode 100644
index 00000000..5b694bec
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECNamedDomainParameters.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+
+public class ECNamedDomainParameters
+ extends ECDomainParameters
+{
+ private ASN1ObjectIdentifier name;
+
+ public ECNamedDomainParameters(ASN1ObjectIdentifier name, ECCurve curve, ECPoint G, BigInteger n)
+ {
+ this(name, curve, G, n, null, null);
+ }
+
+ public ECNamedDomainParameters(ASN1ObjectIdentifier name, ECCurve curve, ECPoint G, BigInteger n, BigInteger h)
+ {
+ this(name, curve, G, n, h, null);
+ }
+
+ public ECNamedDomainParameters(ASN1ObjectIdentifier name, ECCurve curve, ECPoint G, BigInteger n, BigInteger h, byte[] seed)
+ {
+ super(curve, G, n, h, seed);
+
+ this.name = name;
+ }
+
+ public ASN1ObjectIdentifier getName()
+ {
+ return name;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
index 0eb6cb70..29d8b369 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
@@ -3,15 +3,63 @@ package org.bouncycastle.crypto.params;
import org.bouncycastle.crypto.DerivationParameters;
import org.bouncycastle.util.Arrays;
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ * NIST SP800-108 allows for alternative orderings of the input fields, meaning that the input can be formated in multiple ways.
+ * There are 3 supported formats: - Below [i]_2 is a counter of r-bits length concatenated to the fixedInputData.
+ * <ul>
+ * <li>1: K(i) := PRF( KI, [i]_2 || Label || 0x00 || Context || [L]_2 ) with the counter at the very beginning of the fixedInputData (The default implementation has this format)</li>
+ * <li>2: K(i) := PRF( KI, Label || 0x00 || Context || [L]_2 || [i]_2 ) with the counter at the very end of the fixedInputData</li>
+ * <li>3a: K(i) := PRF( KI, Label || 0x00 || [i]_2 || Context || [L]_2 ) OR:</li>
+ * <li>3b: K(i) := PRF( KI, Label || 0x00 || [i]_2 || [L]_2 || Context ) OR:</li>
+ * <li>3c: K(i) := PRF( KI, Label || [i]_2 || 0x00 || Context || [L]_2 ) etc... with the counter somewhere in the 'middle' of the fixedInputData.</li>
+ * </ul>
+ * <p>
+ * This function must be called with the following KDFCounterParameters():
+ * - KI <br/>
+ * - The part of the fixedInputData that comes BEFORE the counter OR null <br/>
+ * - the part of the fixedInputData that comes AFTER the counter OR null <br/>
+ * - the length of the counter in bits (not bytes) <br/>
+ * </p>
+ * Resulting function calls assuming an 8 bit counter.
+ * <ul>
+ * <li>1. KDFCounterParameters(ki, null, "Label || 0x00 || Context || [L]_2]", 8); </li>
+ * <li>2. KDFCounterParameters(ki, "Label || 0x00 || Context || [L]_2]", null, 8); </li>
+ * <li>3a. KDFCounterParameters(ki, "Label || 0x00", "Context || [L]_2]", 8); </li>
+ * <li>3b. KDFCounterParameters(ki, "Label || 0x00", "[L]_2] || Context", 8);</li>
+ * <li>3c. KDFCounterParameters(ki, "Label", "0x00 || Context || [L]_2]", 8); </li>
+ * </ul>
+ */
public final class KDFCounterParameters
implements DerivationParameters
{
- private final byte[] ki;
- private final byte[] fixedInputData;
- private final int r;
+ private byte[] ki;
+ private byte[] fixedInputDataCounterPrefix;
+ private byte[] fixedInputDataCounterSuffix;
+ private int r;
- public KDFCounterParameters(byte[] ki, byte[] fixedInputData, int r)
+ /**
+ * Base constructor - suffix fixed input data only.
+ *
+ * @param ki the KDF seed
+ * @param fixedInputDataCounterSuffix fixed input data to follow counter.
+ * @param r length of the counter in bits.
+ */
+ public KDFCounterParameters(byte[] ki, byte[] fixedInputDataCounterSuffix, int r)
+ {
+ this(ki, null, fixedInputDataCounterSuffix, r);
+ }
+
+ /**
+ * Base constructor - prefix and suffix fixed input data.
+ *
+ * @param ki the KDF seed
+ * @param fixedInputDataCounterPrefix fixed input data to precede counter
+ * @param fixedInputDataCounterSuffix fixed input data to follow counter.
+ * @param r length of the counter in bits.
+ */
+ public KDFCounterParameters(byte[] ki, byte[] fixedInputDataCounterPrefix, byte[] fixedInputDataCounterSuffix, int r)
{
if (ki == null)
{
@@ -19,13 +67,22 @@ public final class KDFCounterParameters
}
this.ki = Arrays.clone(ki);
- if (fixedInputData == null)
+ if (fixedInputDataCounterPrefix == null)
+ {
+ this.fixedInputDataCounterPrefix = new byte[0];
+ }
+ else
+ {
+ this.fixedInputDataCounterPrefix = Arrays.clone(fixedInputDataCounterPrefix);
+ }
+
+ if (fixedInputDataCounterSuffix == null)
{
- this.fixedInputData = new byte[0];
+ this.fixedInputDataCounterSuffix = new byte[0];
}
else
{
- this.fixedInputData = Arrays.clone(fixedInputData);
+ this.fixedInputDataCounterSuffix = Arrays.clone(fixedInputDataCounterSuffix);
}
if (r != 8 && r != 16 && r != 24 && r != 32)
@@ -34,7 +91,7 @@ public final class KDFCounterParameters
}
this.r = r;
}
-
+
public byte[] getKI()
{
return ki;
@@ -42,7 +99,18 @@ public final class KDFCounterParameters
public byte[] getFixedInputData()
{
- return Arrays.clone(fixedInputData);
+ //Retained for backwards compatibility
+ return Arrays.clone(fixedInputDataCounterSuffix);
+ }
+
+ public byte[] getFixedInputDataCounterPrefix()
+ {
+ return Arrays.clone(fixedInputDataCounterPrefix);
+ }
+
+ public byte[] getFixedInputDataCounterSuffix()
+ {
+ return Arrays.clone(fixedInputDataCounterSuffix);
}
public int getR()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/SRP6GroupParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/SRP6GroupParameters.java
new file mode 100644
index 00000000..506560ed
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/SRP6GroupParameters.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.crypto.params;
+
+import java.math.BigInteger;
+
+public class SRP6GroupParameters
+{
+ private BigInteger N, g;
+
+ public SRP6GroupParameters(BigInteger N, BigInteger g)
+ {
+ this.N = N;
+ this.g = g;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getN()
+ {
+ return N;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
index 76241eea..326e106c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
@@ -8,6 +8,7 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
+import java.util.Locale;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.digests.SkeinDigest;
@@ -17,7 +18,7 @@ import org.bouncycastle.util.Integers;
/**
* Parameters for the Skein hash function - a series of byte[] strings identified by integer tags.
- * <p/>
+ * <p>
* Parameterised Skein can be used for:
* <ul>
* <li>MAC generation, by providing a {@link SkeinParameters.Builder#setKey(byte[]) key}.</li>
@@ -179,9 +180,9 @@ public class SkeinParameters
* 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}
+ * <p>
+ * Parameters with type &lt; {@value SkeinParameters#PARAM_TYPE_MESSAGE} are processed before
+ * the message content, parameters with type &gt; {@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.
@@ -227,14 +228,14 @@ public class SkeinParameters
/**
* Implements the recommended personalisation format for Skein defined in Section 4.11 of
* the Skein 1.3 specification.
- * <p/>
+ * <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
+ * @return the current builder.
*/
public Builder setPersonalisation(Date date, String emailAddress, String distinguisher)
{
@@ -258,6 +259,41 @@ public class SkeinParameters
}
/**
+ * Implements the recommended personalisation format for Skein defined in Section 4.11 of
+ * the Skein 1.3 specification. You may need to use this method if the default locale
+ * doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible implementations.
+ * <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 dateLocale locale to be used for date interpretation.
+ * @param emailAddress the email address of the creation of the personalised application.
+ * @param distinguisher an arbitrary personalisation string distinguishing the application.
+ * @return the current builder.
+ */
+ public Builder setPersonalisation(Date date, Locale dateLocale, String emailAddress, String distinguisher)
+ {
+ try
+ {
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ final OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8");
+ final DateFormat format = new SimpleDateFormat("YYYYMMDD", dateLocale);
+ 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)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html
new file mode 100644
index 00000000..4e00a754
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Classes for parameter objects for ciphers and generators.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/parsers/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/parsers/package.html
new file mode 100644
index 00000000..03d05c79
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/parsers/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Helper classes for parsing "on the wire" public keys.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java
index e1ec6c28..94817821 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java
@@ -57,7 +57,7 @@ public class SP800SecureRandom
// check if a reseed is required...
if (drbg.generate(bytes, null, predictionResistant) < 0)
{
- drbg.reseed(entropySource.getEntropy());
+ drbg.reseed(null);
drbg.generate(bytes, null, predictionResistant);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java
index 2146af7f..c7e420db 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java
@@ -1,6 +1,6 @@
package org.bouncycastle.crypto.prng;
-import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Pack;
public class VMPCRandomGenerator implements RandomGenerator
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931RNG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931RNG.java
new file mode 100644
index 00000000..f86dc2d8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931RNG.java
@@ -0,0 +1,146 @@
+package org.bouncycastle.crypto.prng;
+
+import org.bouncycastle.crypto.BlockCipher;
+
+public class X931RNG
+{
+ private static final long BLOCK64_RESEED_MAX = 1L << (16 - 1);
+ private static final long BLOCK128_RESEED_MAX = 1L << (24 - 1);
+ private static final int BLOCK64_MAX_BITS_REQUEST = 1 << (13 - 1);
+ private static final int BLOCK128_MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private final BlockCipher engine;
+ private final EntropySource entropySource;
+
+ private final byte[] DT;
+ private final byte[] I;
+ private final byte[] R;;
+
+ private byte[] V;
+
+ private long reseedCounter = 1;
+
+ /**
+ *
+ * @param engine
+ * @param entropySource
+ */
+ public X931RNG(BlockCipher engine, byte[] dateTimeVector, EntropySource entropySource)
+ {
+ this.engine = engine;
+ this.entropySource = entropySource;
+
+ this.DT = new byte[engine.getBlockSize()];
+
+ System.arraycopy(dateTimeVector, 0, DT, 0, DT.length);
+
+ this.I = new byte[engine.getBlockSize()];
+ this.R = new byte[engine.getBlockSize()];
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ int generate(byte[] output, boolean predictionResistant)
+ {
+ if (R.length == 8) // 64 bit block size
+ {
+ if (reseedCounter > BLOCK64_RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (isTooLarge(output, BLOCK64_MAX_BITS_REQUEST / 8))
+ {
+ throw new IllegalArgumentException("Number of bits per request limited to " + BLOCK64_MAX_BITS_REQUEST);
+ }
+ }
+ else
+ {
+ if (reseedCounter > BLOCK128_RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (isTooLarge(output, BLOCK128_MAX_BITS_REQUEST / 8))
+ {
+ throw new IllegalArgumentException("Number of bits per request limited to " + BLOCK128_MAX_BITS_REQUEST);
+ }
+ }
+
+ if (predictionResistant || V == null)
+ {
+ V = entropySource.getEntropy();
+ }
+
+ int m = output.length / R.length;
+
+ for (int i = 0; i < m; i++)
+ {
+ engine.processBlock(DT, 0, I, 0);
+ process(R, I, V);
+ process(V, R, I);
+
+ System.arraycopy(R, 0, output, i * R.length, R.length);
+
+ increment(DT);
+ }
+
+ int bytesToCopy = (output.length - m * R.length);
+
+ if (bytesToCopy > 0)
+ {
+ engine.processBlock(DT, 0, I, 0);
+ process(R, I, V);
+ process(V, R, I);
+
+ System.arraycopy(R, 0, output, m * R.length, bytesToCopy);
+
+ increment(DT);
+ }
+
+ reseedCounter++;
+
+ return output.length;
+ }
+
+ /**
+ * Reseed the RNG.
+ */
+ void reseed()
+ {
+ V = entropySource.getEntropy();
+ reseedCounter = 1;
+ }
+
+ private void process(byte[] res, byte[] a, byte[] b)
+ {
+ for (int i = 0; i != res.length; i++)
+ {
+ res[i] = (byte)(a[i] ^ b[i]);
+ }
+
+ engine.processBlock(res, 0, res, 0);
+ }
+
+ private void increment(byte[] val)
+ {
+ for (int i = val.length - 1; i >= 0; i--)
+ {
+ if (++val[i] != 0)
+ {
+ break;
+ }
+ }
+ }
+
+ private static boolean isTooLarge(byte[] bytes, int maxBytes)
+ {
+ return bytes != null && bytes.length > maxBytes;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandom.java
new file mode 100644
index 00000000..e51e6f04
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandom.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.crypto.prng;
+
+import java.security.SecureRandom;
+
+public class X931SecureRandom
+ extends SecureRandom
+{
+ private final boolean predictionResistant;
+ private final SecureRandom randomSource;
+ private final X931RNG drbg;
+
+ X931SecureRandom(SecureRandom randomSource, X931RNG drbg, boolean predictionResistant)
+ {
+ this.randomSource = randomSource;
+ this.drbg = drbg;
+ this.predictionResistant = predictionResistant;
+ }
+
+ public void setSeed(byte[] seed)
+ {
+ synchronized (this)
+ {
+ if (randomSource != null)
+ {
+ this.randomSource.setSeed(seed);
+ }
+ }
+ }
+
+ public void setSeed(long seed)
+ {
+ synchronized (this)
+ {
+ // this will happen when SecureRandom() is created
+ if (randomSource != null)
+ {
+ this.randomSource.setSeed(seed);
+ }
+ }
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ synchronized (this)
+ {
+ // check if a reseed is required...
+ if (drbg.generate(bytes, predictionResistant) < 0)
+ {
+ drbg.reseed();
+ drbg.generate(bytes, predictionResistant);
+ }
+ }
+ }
+
+ public byte[] generateSeed(int numBytes)
+ {
+ byte[] bytes = new byte[numBytes];
+
+ this.nextBytes(bytes);
+
+ return bytes;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandomBuilder.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandomBuilder.java
new file mode 100644
index 00000000..89c29055
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/X931SecureRandomBuilder.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.crypto.prng;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Pack;
+
+public class X931SecureRandomBuilder
+{
+ private SecureRandom random; // JDK 1.1 complains on final.
+ private EntropySourceProvider entropySourceProvider;
+
+ private BlockCipher engine;
+ private byte[] dateTimeVector;
+
+ /**
+ * Basic constructor, creates a builder using an EntropySourceProvider based on the default SecureRandom with
+ * predictionResistant set to false.
+ * <p>
+ * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if
+ * the default SecureRandom does for its generateSeed() call.
+ * </p>
+ */
+ public X931SecureRandomBuilder()
+ {
+ this(new SecureRandom(), false);
+ }
+
+ /**
+ * Construct a builder with an EntropySourceProvider based on the passed in SecureRandom and the passed in value
+ * for prediction resistance.
+ * <p>
+ * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if
+ * the passed in SecureRandom does for its generateSeed() call.
+ * </p>
+ * @param entropySource
+ * @param predictionResistant
+ */
+ public X931SecureRandomBuilder(SecureRandom entropySource, boolean predictionResistant)
+ {
+ this.random = entropySource;
+ this.entropySourceProvider = new BasicEntropySourceProvider(random, predictionResistant);
+ }
+
+ /**
+ * Create a builder which makes creates the SecureRandom objects from a specified entropy source provider.
+ * <p>
+ * <b>Note:</b> If this constructor is used any calls to setSeed() in the resulting SecureRandom will be ignored.
+ * </p>
+ * @param entropySourceProvider a provider of EntropySource objects.
+ */
+ public X931SecureRandomBuilder(EntropySourceProvider entropySourceProvider)
+ {
+ this.random = null;
+ this.entropySourceProvider = entropySourceProvider;
+ }
+
+ public X931SecureRandomBuilder setDateTimeVector(byte[] dateTimeVector)
+ {
+ this.dateTimeVector = dateTimeVector;
+
+ return this;
+ }
+
+ /**
+ * Construct a X9.31 secure random generator using the passed in engine and key. If predictionResistant is true the
+ * generator will be reseeded on each request.
+ *
+ * @param engine a block cipher to use as the operator.
+ * @param key the block cipher key to initialise engine with.
+ * @param predictionResistant true if engine to be reseeded on each use, false otherwise.
+ * @return a SecureRandom.
+ */
+ public X931SecureRandom build(BlockCipher engine, KeyParameter key, boolean predictionResistant)
+ {
+ this.engine = engine;
+
+ if (dateTimeVector == null)
+ {
+ if (engine.getBlockSize() == 8)
+ {
+ dateTimeVector = Pack.longToBigEndian(System.currentTimeMillis());
+ }
+ else
+ {
+ dateTimeVector = new byte[engine.getBlockSize()];
+ byte[] date = Pack.longToBigEndian(System.currentTimeMillis());
+ System.arraycopy(date, 0, dateTimeVector, 0, date.length);
+ }
+ }
+
+ engine.init(true, key);
+
+ return new X931SecureRandom(random, new X931RNG(engine, dateTimeVector, entropySourceProvider.get(engine.getBlockSize() * 8)), predictionResistant);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java
index 84fe4a40..57011996 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java
@@ -310,6 +310,16 @@ public class CTRSP800DRBG
}
/**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int getBlockSize()
+ {
+ return _V.length * 8;
+ }
+
+ /**
* Populate a passed in array with random data.
*
* @param output output array for generated bits.
@@ -365,17 +375,20 @@ public class CTRSP800DRBG
_engine.init(true, new KeyParameter(expandKey(_Key)));
- for (int i = 0; i < output.length / out.length; i++)
+ for (int i = 0; i <= output.length / out.length; i++)
{
- addOneTo(_V);
-
- _engine.processBlock(_V, 0, out, 0);
-
int bytesToCopy = ((output.length - i * out.length) > out.length)
? out.length
: (output.length - i * _V.length);
- System.arraycopy(out, 0, output, i * out.length, bytesToCopy);
+ if (bytesToCopy != 0)
+ {
+ addOneTo(_V);
+
+ _engine.processBlock(_V, 0, out, 0);
+
+ System.arraycopy(out, 0, output, i * out.length, bytesToCopy);
+ }
}
CTR_DRBG_Update(additionalInput, _Key, _V);
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
index c3715bc1..7dcfa94f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java
@@ -19,7 +19,7 @@ public class DualECPoints
* <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.
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 8d326ffd..4e1b881d 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,9 @@ 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.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
@@ -70,6 +72,7 @@ public class DualECSP800DRBG
private ECPoint _Q;
private byte[] _s;
private int _sLength;
+ private ECMultiplier _fixedPointMultiplier = new FixedPointCombMultiplier();
/**
* Construct a SP800-90A Dual EC DRBG.
@@ -146,6 +149,16 @@ public class DualECSP800DRBG
}
/**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int getBlockSize()
+ {
+ return _outlen * 8;
+ }
+
+ /**
* Populate a passed in array with random data.
*
* @param output output array for generated bits.
@@ -199,7 +212,7 @@ public class DualECSP800DRBG
//System.err.println("S: " + new String(Hex.encode(_s)));
- byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray();
+ byte[] r = getScalarMultipleXCoord(_Q, s).toByteArray();
if (r.length > _outlen)
{
@@ -220,7 +233,7 @@ public class DualECSP800DRBG
{
s = getScalarMultipleXCoord(_P, s);
- byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray();
+ byte[] r = getScalarMultipleXCoord(_Q, s).toByteArray();
int required = output.length - outOffset;
@@ -237,7 +250,7 @@ public class DualECSP800DRBG
}
// Need to preserve length of S as unsigned int.
- _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(s).normalize().getAffineXCoord().toBigInteger());
+ _s = BigIntegers.asUnsignedByteArray(_sLength, getScalarMultipleXCoord(_P, s));
return numberOfBits;
}
@@ -302,6 +315,6 @@ public class DualECSP800DRBG
private BigInteger getScalarMultipleXCoord(ECPoint p, BigInteger s)
{
- return p.multiply(s).normalize().getAffineXCoord().toBigInteger();
+ return _fixedPointMultiplier.multiply(p, s).normalize().getAffineXCoord().toBigInteger();
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java
index 3ddeaac6..f4ef2c45 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java
@@ -88,6 +88,16 @@ public class HMacSP800DRBG
}
/**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each round of the DRBG.
+ */
+ public int getBlockSize()
+ {
+ return _V.length * 8;
+ }
+
+ /**
* Populate a passed in array with random data.
*
* @param output output array for generated bits.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java
index 4ed57163..d6ab4f53 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java
@@ -88,6 +88,16 @@ public class HashSP800DRBG
}
/**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int getBlockSize()
+ {
+ return _digest.getDigestSize() * 8;
+ }
+
+ /**
* Populate a passed in array with random data.
*
* @param output output array for generated bits.
@@ -226,12 +236,17 @@ public class HashSP800DRBG
private byte[] hash(byte[] input)
{
- _digest.update(input, 0, input.length);
byte[] hash = new byte[_digest.getDigestSize()];
- _digest.doFinal(hash, 0);
+ doHash(input, hash);
return hash;
}
-
+
+ private void doHash(byte[] input, byte[] output)
+ {
+ _digest.update(input, 0, input.length);
+ _digest.doFinal(output, 0);
+ }
+
// 1. m = [requested_number_of_bits / outlen]
// 2. data = V.
// 3. W = the Null string.
@@ -251,10 +266,10 @@ public class HashSP800DRBG
byte[] W = new byte[lengthInBits / 8];
- byte[] dig;
+ byte[] dig = new byte[_digest.getDigestSize()];
for (int i = 0; i <= m; i++)
{
- dig = hash(data);
+ doHash(data, dig);
int bytesToCopy = ((W.length - i * dig.length) > dig.length)
? dig.length
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java
index 93bc8945..7a919f31 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java
@@ -6,6 +6,13 @@ package org.bouncycastle.crypto.prng.drbg;
public interface SP80090DRBG
{
/**
+ * Return the block size of the DRBG.
+ *
+ * @return the block size (in bits) produced by each round of the DRBG.
+ */
+ int getBlockSize();
+
+ /**
* Populate a passed in array with random data.
*
* @param output output array for generated bits.
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
new file mode 100644
index 00000000..c0061660
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+SP800-90A deterministic random bit generators, can be used stand alone or in conjunction with SP800SecureRandomBuilder class.
+</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
new file mode 100644
index 00000000..bb583abd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Lightweight psuedo-random number generators and SecureRandom builders.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/AllTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/AllTests.java
new file mode 100644
index 00000000..4c0504f8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/AllTests.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.crypto.prng.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.bouncycastle.util.test.SimpleTestResult;
+
+public class AllTests
+ extends TestCase
+{
+ public void testCrypto()
+ {
+ org.bouncycastle.util.test.Test[] tests = RegressionTest.tests;
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ SimpleTestResult result = (SimpleTestResult)tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ fail(result.toString());
+ }
+ }
+ }
+
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("Lightweight Crypto PRNG Tests");
+
+ suite.addTestSuite(AllTests.class);
+
+ return suite;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java
new file mode 100644
index 00000000..3d37a780
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java
@@ -0,0 +1,528 @@
+package org.bouncycastle.crypto.prng.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.params.DESedeParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG;
+import org.bouncycastle.crypto.prng.drbg.SP80090DRBG;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * CTR DRBG Test
+ */
+public class CTRDRBGTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "CTRDRBGTest";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new CTRDRBGTest());
+ }
+
+ private DRBGTestVector[] createTestVectorData()
+ {
+ return new DRBGTestVector[]
+ {
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "ABC88224514D0316EA3D48AEE3C9A2B4",
+ "D3D3F372E43E7ABDC4FA293743EED076"
+ }
+ ),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "D4564EE072ACA5BD279536E14F94CB12",
+ "1CCD9AFEF15A9679BA75E35225585DEA"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "760BED7D92B083B10AF31CF0656081EB",
+ "FD1AC41482384D823CF3FD6F0E6C88B3"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "7A4C1D7ADC8A67FDB50100ED23583A2C",
+ "43044D311C0E07541CA5C8B0916976B2"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "8FB78ABCA75C9F284E974E36141866BC",
+ "9D9745FF31C42A4488CBB771B13B5D86"
+ }
+ ),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "0E389920A09B485AA4ABD0CA7E60D89C",
+ "F4478EC6659A0D3577625B0C73A211DD"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "64983055D014550B39DE699E43130B64",
+ "035FDDA8582A2214EC722C410A8D95D3"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C"),
+ new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "A29C1A8C42FBC562D7D1DBA7DC541FFE",
+ "0BDA66B049429061C013E4228C2F44C6"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBC"),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "8CF59C8CF6888B96EB1C1E3E79D82387AF08A9E5FF75E23F1FBCD4559B6B997E",
+ "69CDEF912C692D61B1DA4C05146B52EB7B8849BD87937835328254EC25A9180E"
+ }
+ ),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "E8C74A4B7BFFB53BEB80E78CA86BB6DF70E2032AEB473E0DD54D2339CEFCE9D0",
+ "26B3F823B4DBAFC23B141375E10B3AEB7A0B5DEF1C7D760B6F827D01ECD17AC7"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "18FDEFBDC43D7A36D5D6D862205765D1D701C9F237007030DF1B8E70EE4EEE29",
+ "9888F1D38BB1CCE31B363AA1BD9B39616876C30DEE1FF0B7BD8C4C441715C833"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "BFF4B85D68C84529F24F69F9ACF1756E29BA648DDEB825C225FA32BA490EF4A9",
+ "9BD2635137A52AF7D0FCBEFEFB97EA93A0F4C438BD98956C0DACB04F15EE25B3"
+ }
+ ),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "4573AC8BBB33D7CC4DBEF3EEDF6EAE748B536C3A1082CEE4948CDB51C83A7F9C",
+ "99C628CDD87BD8C2F1FE443AA7F761DA16886436326323354DA6311FFF5BC678"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 128,
+ new Bit256EntropyProvider().get(256),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "F324104E2FA14F79D8AA60DF06B93B3BC157324958F0A7EE1E193677A70E0250",
+ "78F4C840134F40DC001BFAD3A90B5EF4DEBDBFAC3CFDF0CD69A89DC4FD34713F"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"),
+ new DRBGTestVector(
+ new AESFastEngine(), 192,
+ new Bit320EntropyProvider().get(320),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "E231244B3235B085C81604424357E85201E3828B5C45568679A5555F867AAC8C",
+ "DDD0F7BCCADADAA31A67652259CE569A271DD85CF66C3D6A7E9FAED61F38D219"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667"),
+ new DRBGTestVector(
+ new AESFastEngine(), 192,
+ new Bit320EntropyProvider().get(320),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "F780D4A2C25CF8EE7407D948EC0B724A4235D8B20E65081392755CA7912AD7C0",
+ "BA14617F915BA964CB79276BDADC840C14B631BBD1A59097054FA6DFF863B238"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6061626364656667"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "47111E146562E9AA2FB2A1B095D37A8165AF8FC7CA611D632BE7D4C145C83900",
+ "98A28E3B1BA363C9DAF0F6887A1CF52B833D3354D77A7C10837DD63DD2E645F8"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "71BB3F9C9CEAF4E6C92A83EB4C7225010EE150AC75E23F5F77AD5073EF24D88A",
+ "386DEBBBF091BBF0502957B0329938FB836B82E594A2F5FDD5EB28D4E35528F4"
+ }
+ )
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "1A2E3FEE9056E98D375525FDC2B63B95B47CE51FCF594D804BD5A17F2E01139B",
+ "601F95384F0D85946301D1EACE8F645A825CE38F1E2565B0C0C439448E9CA8AC"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "EAE6BCE781807E524D26605EA198077932D01EEB445B9AC6C5D99C101D29F46E",
+ "738E99C95AF59519AAD37FF3D5180986ADEBAB6E95836725097E50A8D1D0BD28"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"),
+ new DRBGTestVector(
+ new AESFastEngine(), 256,
+ new Bit384EntropyProvider().get(384),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "eae6bce781807e524d26605ea198077932d01eeb445b9ac6c5d99c101d29f46e30b27377",
+ "ec51b55b49904c3ff9e13939f1cf27398993e1b3acb2b0be0be8761261428f0aa8ba2657"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECF")
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DRBGTestVector[] tests = createTestVectorData();
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ DRBGTestVector tv = tests[i];
+
+ byte[] nonce = tv.nonce();
+ byte[] personalisationString = tv.personalizationString();
+
+ SP80090DRBG d = new CTRSP800DRBG(tv.getCipher(), tv.keySizeInBits(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce);
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output)));
+ }
+
+ output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(1), tv.predictionResistance());
+
+ expected = tv.expectedValue(1);
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output)));
+ }
+ }
+
+ // DESede/TDEA key parity test
+ DRBGTestVector tv = tests[0];
+
+ SP80090DRBG drbg = new CTRSP800DRBG(new KeyParityCipher(tv.getCipher()), tv.keySizeInBits(), tv.securityStrength(), tv.entropySource(), tv.personalizationString(), tv.nonce());
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ drbg.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ // Exception tests
+ SP80090DRBG d;
+ try
+ {
+ d = new CTRSP800DRBG(new AESEngine(), 256, 256, new Bit232EntropyProvider().get(128), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Not enough entropy for security strength required"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new CTRSP800DRBG(new DESedeEngine(), 256, 256, new Bit232EntropyProvider().get(232), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new CTRSP800DRBG(new DESedeEngine(), 168, 256, new Bit232EntropyProvider().get(232), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new CTRSP800DRBG(new AESEngine(), 192, 256, new Bit232EntropyProvider().get(232), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+ }
+
+ private class Bit232EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit232EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C" +
+ "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC"), true);
+ }
+ }
+
+ private class Bit256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit256EntropyProvider()
+ {
+ super(Hex.decode(
+ "0001020304050607"+
+ "08090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"+
+ "8081828384858687"+
+ "88898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F"+
+ "C0C1C2C3C4C5C6C7"+
+ "C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"), true);
+ }
+ }
+
+ private class Bit320EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit320EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F"+
+ "101112131415161718191A1B1C1D1E1F2021222324252627"+
+ "808182838485868788898A8B8C8D8E8F"+
+ "909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7"+
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+
+ "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7"), true);
+ }
+ }
+
+ private class Bit384EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit384EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F1011121314151617" +
+ "18191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F" +
+ "808182838485868788898A8B8C8D8E8F9091929394959697" +
+ "98999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAF" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7" +
+ "D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"), true);
+ }
+ }
+
+ private class KeyParityCipher
+ implements BlockCipher
+ {
+ private BlockCipher cipher;
+
+ KeyParityCipher(BlockCipher cipher)
+ {
+ this.cipher = cipher;
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ byte[] k = Arrays.clone(((KeyParameter)params).getKey());
+
+ DESedeParameters.setOddParity(k);
+
+ if (!Arrays.areEqual(((KeyParameter)params).getKey(), k))
+ {
+ fail("key not odd parity");
+ }
+
+ cipher.init(forEncryption, params);
+ }
+
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ public int getBlockSize()
+ {
+ return cipher.getBlockSize();
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException
+ {
+ return cipher.processBlock(in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ cipher.reset();
+ }
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/DRBGTestVector.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/DRBGTestVector.java
new file mode 100644
index 00000000..dd6801c5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/DRBGTestVector.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.crypto.prng.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.prng.EntropySource;
+import org.bouncycastle.util.encoders.Hex;
+
+public class DRBGTestVector
+{
+ private Digest _digest;
+ private BlockCipher _cipher;
+ private int _keySizeInBits;
+ private EntropySource _eSource;
+ private boolean _pr;
+ private String _nonce;
+ private String _personalisation;
+ private int _ss;
+ private String[] _ev;
+ private List _ai = new ArrayList();
+
+ public DRBGTestVector(Digest digest, EntropySource eSource, boolean predictionResistance, String nonce, int securityStrength, String[] expected)
+ {
+ _digest = digest;
+ _eSource = eSource;
+ _pr = predictionResistance;
+ _nonce = nonce;
+ _ss = securityStrength;
+ _ev = expected;
+ _personalisation = null;
+ }
+
+ public DRBGTestVector(BlockCipher cipher, int keySizeInBits, EntropySource eSource, boolean predictionResistance, String nonce, int securityStrength, String[] expected)
+ {
+ _cipher = cipher;
+ _keySizeInBits = keySizeInBits;
+ _eSource = eSource;
+ _pr = predictionResistance;
+ _nonce = nonce;
+ _ss = securityStrength;
+ _ev = expected;
+ _personalisation = null;
+ }
+
+ public Digest getDigest()
+ {
+ return _digest;
+ }
+
+ public BlockCipher getCipher()
+ {
+ return _cipher;
+ }
+
+ public int keySizeInBits()
+ {
+ return _keySizeInBits;
+ }
+
+ public DRBGTestVector addAdditionalInput(String input)
+ {
+ _ai.add(input);
+
+ return this;
+ }
+
+ public DRBGTestVector setPersonalizationString(String p)
+ {
+ _personalisation = p;
+
+ return this;
+ }
+
+ public EntropySource entropySource()
+ {
+ return _eSource;
+ }
+
+ public boolean predictionResistance()
+ {
+ return _pr;
+ }
+
+ public byte[] nonce()
+ {
+ if (_nonce == null)
+ {
+ return null;
+ }
+
+ return Hex.decode(_nonce);
+ }
+
+ public byte[] personalizationString()
+ {
+ if (_personalisation == null)
+ {
+ return null;
+ }
+
+ return Hex.decode(_personalisation);
+ }
+
+ public int securityStrength()
+ {
+ return _ss;
+ }
+
+ public byte[] expectedValue(int index)
+ {
+ return Hex.decode(_ev[index]);
+ }
+
+ public byte[] additionalInput(int position)
+ {
+ int len = _ai.size();
+ byte[] rv;
+ if (position >= len)
+ {
+ rv = null;
+ }
+ else
+ {
+ rv = Hex.decode((String)(_ai.get(position)));
+ }
+ return rv;
+ }
+
+ }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/DualECDRBGTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/DualECDRBGTest.java
new file mode 100644
index 00000000..d6c26307
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/DualECDRBGTest.java
@@ -0,0 +1,415 @@
+package org.bouncycastle.crypto.prng.test;
+
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG;
+import org.bouncycastle.crypto.prng.drbg.SP80090DRBG;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Dual EC SP800-90 DRBG test
+ */
+public class DualECDRBGTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "DualECDRBG";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new DualECDRBGTest());
+ }
+
+ private DRBGTestVector[] createTestVectorData()
+ {
+ return new DRBGTestVector[]
+ {
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "FF5163C388F791E96F1052D5C8F0BD6FBF7144839C4890FF85487C5C12702E4C9849AF518AE68DEB14D3A62702BBDE4B98AB211765FD87ACA12FC2A6",
+ "9A0A11F2DFB88F7260559DD8DA6134EB2B34CC0415FA8FD0474DB6B85E1A08385F41B435DF81296B1B4EDF66E0107C0844E3D28A89B05046B89177F2"
+ }),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "C08E954FCD486D0B0934A0236692AC705A835D1A3C94D2ACD4684AB26E978D7D42E73CC06D6EC1472C63E51BED7F71518395836E2052BBD73A20CABB",
+ "1D76DEE36FCC5F9478C112EAFA1C4CCD0635435A6F3A247A3BA3849790B5245070E95C1A67BE7A39BFB213F2C0EFCC171A3253DA6D54DA4362EA2099"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "3AB095CC493A8730D70DE923108B2E4710799044FFC27D0A1156250DDF97E8B05ACE055E49F3E3F5B928CCD18317A3E68FCB0B6F0459ADF9ECF79C87",
+ "7B902FC35B0AF50F57F8822936D08A96E41B16967C6B1AA0BC05032F0D53919DC587B664C883E2FE8F3948002FCD8BCBFC4706BCAA2075EF6BF41167"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "3B68A1D95ED0312150AC1991189780F37EC50E75249F915CD806BBA0C44F9E3A919B2390805E1E90C1D2D1C823B17B96DB44535B72E0CFB62723529D",
+ "250B933475E3BD4FC85D97FD797834B599DEDEDF8B6F15474E1F31B4AF215CFA7A8C0A0296A2E374B3886BB0CC7E49DBB19324564B451E64F12864F9"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "8C77288EDBEA9A742464F78D55E33593C1BF5F9D8CD8609D6D53BAC4E4B42252A227A99BAD0F2358B05955CD35723B549401C71C9C1F32F8A2018E24",
+ "56ECA61C64F69C1C232E992623C71418BD0B96D783118FAAD94A09E3A9DB74D15E805BA7F14625995CA77612B2EF7A05863699ECBABF70D3D422C014"
+ }),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "A5C397DFEB540E86F0470E9625D5C5AC2D50016FB201E8DF574F2201DFBB42A799FEB9E238AAD301A493382250EEE60D2E2927E500E848E57535ABD1",
+ "BF9894630BEBAF0A0EDFE726285EB055FD2ED678B76673803DD327F49DBEDE87D3E447A6EB73B5D5C52A40078132677F412E9E7DE32B9B1CB32421B9"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "1F858858B65357D6360E1ED8F8475767B08DAB30718CCA01C6FAE77A4BDCE2702C76D0FB4758EA1ED6AA587CFD26B9011DC8A75D0B4154193BB2C1798FFA52BCAB208310" +
+ "3CD2AAD44BEED56D042FC2B8915D7D9BED6437EFEB1582EE",
+ "6E4AAB63938212C870F24BB067A32CA9E7FC2343" +
+ "5D411729268C8BA6F90E87074D04888CE2CC5A916B7AC93F" +
+ "EDE85E2995645DFCC4CE44B9FB41F1BFCC5E9F59EE3A8E1B" +
+ "8F85247F741B7C480521EE6BF8BA319B59048E65F08FAA76"
+ }),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "E6A30AB0C9AFCBA673E4F1C94B3DB1F0C7D78B3D" +
+ "87B967281BE1E7B3CAF5200AED502C26B84FC169FE8336BD" +
+ "23271CB299812F2CF1955AA63FC362044ABA246EF1610F9E" +
+ "DC613924A84A00F8DB3FC65C13373F3171EB20848FA9A70E",
+ "8585764DF1C86EA12ACCB882525BF6217B447486" +
+ "5EBFDA367B8657FA80471139BAC626172B9F219DF2CE9099" +
+ "F65833E07CD1A8DD80468779EA3C26620A2C9C9F5C7EFCDD" +
+ "C036E6F6C8BF70316D3C37FC246A4CC79B3F1DB971D72ED0"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F5051525354555657"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "13F6EA9BBA7BABDC2A52A3B9FD73D65ECAA638A0" +
+ "4C74BCCA2ACDE6FD29FEA4B5D884E095E87D1B7C0DEB9D37" +
+ "7AD81FBFEEA2D5EF82C0F6F52B9FCC359E769AC9DF2A876C" +
+ "58BAF21657814F3E66D1680B1D4EBD65581E42534F85197D",
+ "FC0A36F4D20F8F83BE3430AA3C36A49191821A82" +
+ "072BBC3D5AFF8D7EC39484D646277CE87599B6FE8CCA9862" +
+ "559703A10F4DE1066BFD30B80C325E774B512525BC6D3734" +
+ "4C93906368243D31F89E99C4D2A6E9BEB24D5F7267360DCA"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F5051525354555657")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F7071727374757677")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "FE55601BF734493013705CCEB76E44AAD48373F7" +
+ "42E72B83D4701FA6549255F1CDE6217953522FF973BA4F6E" +
+ "C96D2BDCF14A76BE7DEB61781E34B99335BD714F17C91739" +
+ "B4E2AB57E36E9C3116E215D3D94FCFAD532636874875CAC7",
+ "F5E59D0ABADE81F62FFAB9D4A6A26FF200016608" +
+ "A7215E389858FFED83FBC75CFD33DBA6688C89AA32AD22E4" +
+ "80EA3D04EADFB35567B67564207E64B77844E8E4A87502D5" +
+ "02DBBB6D8277F1CACDB7CF8D293D09DB7DD59A950821507A"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F7071727374757677")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(192),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "CC788F70FB08F256D9604333630D85936D400F45" +
+ "718DC3F939A8B9F6F75D3E4EC17D68FBB924AEACB7021295" +
+ "48FA63CE9BCB82176639B64DE890A47025B5582312FE934E" +
+ "F0D0A12697C0F05D2DA108CCADB511BA0EB62F4051BB2354",
+ "2C922EA620D76E4137B315EBC29E518F80951B3F" +
+ "0E6173FA2BFD94A230EE513EE2E4EB330D802F620DD24911" +
+ "534EC0F95A1F1D44A2125F5D57476A666FC372092B55D0D6" +
+ "8B49738F5BC466EC206AB3CF6A972B38BCFAE5FCD53C7E21 "
+ }),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(256),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "7A8313798EE1" +
+ "D1898712683F2D0B0DEE5804146ABA64FDA8DB4E539CC8D1" +
+ "E59C74EE5AA48E73E958C8EC85DD529D42E68B4F7E02FFAF" +
+ "3E3EF8312AEA68BC08A414885E60A7DF0B55F9D90210B319" +
+ "E9B8FD23E078A4153636F29AA3CAC8198CB1D5D846151653" +
+ "ECE275A591089261238014E5058410065AB8229EB9115E8E",
+ "918B5D79E646" +
+ "64966D954BC5E2946BF48F061BF0C2701C3C2D1F75EA821E" +
+ "1DA05D5B3C2C4EEA246E806B53BF6BDB3F3D53A3AE756C2A" +
+ "45C72603973A3DE1BC367C283CA124A5589CEAB30E5D2D74" +
+ "8A40DD874FF15B032CF4F4B2AAD590B0DB91A0D38FCE93C5" +
+ "AAD4E55AC482F86FF06FAE66B7C7CCA7E45557E1A5A3B85D"
+ }),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(256),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "C7ED88A2C690" +
+ "1C04802BA2BB04262921B19664835A4A3C002CB9F13E35E3" +
+ "DEB3698A436BF1C85B070E9E6977CA78A5130905AA0C01A9" +
+ "4130F5133DF904A4ACF59A7DD01227E8FCA1C8D51F093839" +
+ "46ECD950113104760D7E216CAF581FE9D3AACE6FC4CDDC4C" +
+ "CD736D26A60BE8BE2A6A78CD752D1EC7CCC802638B177307",
+ "83B78B206785" +
+ "4412EEB24AEA86064D510C68FD96DBF94EAC1BC2022752D7" +
+ "558AEB9F97B9CBC1B9648FE4D88E2C82A6F530675E1DB92D" +
+ "396D6D85BDAD2A23CBD10AD808ECCCFBFC811EB68AE835E4" +
+ "912E011DD10A4399C8DE2D9D88F81B6168B05D282B9DAC1E" +
+ "65E0A45F61043E1FA047870DD582295E6C50DD1185B13594 "
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F")
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(256),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "CC7035C73040" +
+ "5CF5DF7137ED9E10744B75B540AFFC68EB564B71C0F737E8" +
+ "F656B6171940497FA90D8F383EFB6FC6717BA14AAA164EF5" +
+ "6641C0F513312551DCD21D0A5B0DBDCD97F627E968DFD752" +
+ "56C11CF2BCCA5822EAACE796A34CB7D2F8CD8CC6DBE76274" +
+ "498289BBC4C2F1CADA6185D82605CF992EC285BC4945EE9E",
+ "0E6C329AD1BE" +
+ "681EB1E6F5E03A89E3D80153D6CCDD5A3ECF865003EE4A2D" +
+ "E5A23B7F43681361CFAFC3A3FEF17777E75CF9D6685573C8" +
+ "87A3962CB955076D45D6F1E45EE4B8CB31A4731CDA031FA2" +
+ "815B6D34E29F2603526CE186576F4CCA3FEDF7F8ACDB37C9" +
+ "9D762706ABE4967D44739C8CFCFCC76C58B1ED243AC394C0"
+ }),
+ // From http://csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors.zip
+ // modified to test partial block processing.
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new TestEntropySourceProvider(Hex.decode("a826f1cd3fa24b9e71c316e5bf2bafff"), false).get(128),
+ false,
+ "82bc3bf050614b34",
+ 128,
+ new String[]
+ {
+ "14949b876e30f832331f59f2e687350bea9ba22b78549521a70748ca916c74ebff0b638266aa" +
+ "d81e089545eb60bfe332f7d134d91ed3c104f975fae0f71391add71e3380a725251ed5552a84" +
+ "650637eddfc88b5ab26311277cbc429aa152b2cfac61c67846512d7564114177a622f25e870a" +
+ "acec37c0977d",
+ "7050bf74a887809673ecd295071f7a457d1e2e227f68ef4b4445e34f3904b95d4833180ee522" +
+ "104bfc996234063e2c76173937b883c66b0e64a56643877228cad5212cddbf839270ef80889b" +
+ "c83424c141c2419f2231004c8860f8fd95435e2c9f8ac7409fcbfb6a74851fadc7d99bf5d68b" +
+ "591892f0e3a1"
+ }),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new TestEntropySourceProvider(Hex.decode("a826f1cd3fa24b9e71c316e5bf2bafff"), false).get(128),
+ false,
+ "82bc3bf050614b34",
+ 128,
+ new String[]
+ {
+ "14949b876e30f832331f59f2e687350bea9ba22b78549521a70748ca916c74ebff0b638266aa" +
+ "d81e089545eb60bfe332f7d134d91ed3c104f975fae0f71391add71e3380a725251ed5552a84" +
+ "650637eddfc88b5ab26311277cbc429aa152b2cfac61c67846512d7564114177a622f25e870a" +
+ "acec37c0977d",
+ "7050bf74a887809673ecd295071f7a457d1e2e227f68ef4b4445e34f3904b95d4833180ee522" +
+ "104bfc996234063e2c76173937b883c66b0e64a56643877228cad5212cddbf839270ef80889b" +
+ "c83424c141c2419f2231004c8860f8fd95435e2c9f8ac7409fcbfb6a74851fadc7d99bf5d68b" +
+ "591892f0e3"
+ })
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DRBGTestVector[] tests = createTestVectorData();
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ DRBGTestVector tv = tests[i];
+
+ byte[] nonce = tv.nonce();
+ byte[] personalisationString = tv.personalizationString();
+
+ SP80090DRBG d = new DualECSP800DRBG(tv.getDigest(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce);
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output)));
+ }
+
+ output = new byte[tv.expectedValue(1).length];
+
+ d.generate(output, tv.additionalInput(1), tv.predictionResistance());
+
+ expected = tv.expectedValue(1);
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output)));
+ }
+ }
+
+ // Exception tests
+ //
+ SP80090DRBG d;
+
+ try
+ {
+ d = new DualECSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(128), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("EntropySource must provide between 256 and 4096 bits"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new DualECSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(1 << (13 - 1) + 1), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("EntropySource must provide between 256 and 4096 bits"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new DualECSP800DRBG(new SHA1Digest(), 256, new SHA256EntropyProvider().get(256), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by digest"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+ }
+
+ private class SHA256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA256EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F " +
+ "808182838485868788898A8B8C8D8E8F" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"), true);
+ }
+ }
+
+ private class SHA384EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA384EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F1011121314151617" +
+ "808182838485868788898A8B8C8D8E8F9091929394959697" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7"), true);
+ }
+ }
+
+ private class SHA512EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA512EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" +
+ "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"), true);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/FixedSecureRandomTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/FixedSecureRandomTest.java
new file mode 100644
index 00000000..24fc238a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/FixedSecureRandomTest.java
@@ -0,0 +1,68 @@
+package org.bouncycastle.crypto.prng.test;
+
+import org.bouncycastle.crypto.prng.FixedSecureRandom;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class FixedSecureRandomTest
+ extends SimpleTest
+{
+ byte[] base = Hex.decode("deadbeefdeadbeef");
+ byte[] r1 = Hex.decode("cafebabecafebabe");
+ byte[] r2 = Hex.decode("ffffffffcafebabedeadbeef");
+
+ public String getName()
+ {
+ return "FixedSecureRandom";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ FixedSecureRandom fixed = new FixedSecureRandom(base);
+ byte[] buf = new byte[8];
+
+ fixed.nextBytes(buf);
+
+ if (!Arrays.areEqual(buf, base))
+ {
+ fail("wrong data returned");
+ }
+
+ fixed = new FixedSecureRandom(base);
+
+ byte[] seed = fixed.generateSeed(8);
+
+ if (!Arrays.areEqual(seed, base))
+ {
+ fail("wrong seed data returned");
+ }
+
+ if (!fixed.isExhausted())
+ {
+ fail("not exhausted");
+ }
+
+ fixed = new FixedSecureRandom(new byte[][] { r1, r2 });
+
+ seed = fixed.generateSeed(12);
+
+ if (!Arrays.areEqual(seed, Hex.decode("cafebabecafebabeffffffff")))
+ {
+ fail("wrong seed data returned - composite");
+ }
+
+ fixed.nextBytes(buf);
+
+ if (!Arrays.areEqual(buf, Hex.decode("cafebabedeadbeef")))
+ {
+ fail("wrong data returned");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new FixedSecureRandomTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/HMacDRBGTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/HMacDRBGTest.java
new file mode 100644
index 00000000..add77d5a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/HMacDRBGTest.java
@@ -0,0 +1,508 @@
+package org.bouncycastle.crypto.prng.test;
+
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG;
+import org.bouncycastle.crypto.prng.drbg.SP80090DRBG;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * HMAC SP800-90 DRBG
+ */
+public class HMacDRBGTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "HMacDRBG";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HMacDRBGTest());
+ }
+
+ private DRBGTestVector[] createTestVectorData()
+ {
+ return new DRBGTestVector[]
+ {
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "5A7D3B449F481CB38DF79AD2B1FCC01E57F8135E8C0B22CD0630BFB0127FB5408C8EFC17A929896E",
+ "82cf772ec3e84b00fc74f5df104efbfb2428554e9ce367d03aeade37827fa8e9cb6a08196115d948"
+ }),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "B3BD05246CBA12A64735A4E3FDE599BC1BE30F439BD060208EEA7D71F9D123DF47B3CE069D98EDE6",
+ "B5DADA380E2872DF935BCA55B882C8C9376902AB639765472B71ACEBE2EA8B1B6B49629CB67317E0"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "C7AAAC583C6EF6300714C2CC5D06C148CFFB40449AD0BB26FAC0497B5C57E161E36681BCC930CE80",
+ "6EBD2B7B5E0A2AD7A24B1BF9A1DBA47D43271719B9C37B7FE81BA94045A14A7CB514B446666EA5A7"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "FEC4597F06A3A8CC8529D59557B9E661053809C0BC0EFC282ABD87605CC90CBA9B8633DCB1DAE02E",
+ "84ADD5E2D2041C01723A4DE4335B13EFDF16B0E51A0AD39BD15E862E644F31E4A2D7D843E57C5968"
+ }),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "6C37FDD729AA40F80BC6AB08CA7CC649794F6998B57081E4220F22C5C283E2C91B8E305AB869C625",
+ "CAF57DCFEA393B9236BF691FA456FEA7FDF1DF8361482CA54D5FA723F4C88B4FA504BF03277FA783"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "A1BA8FA58BB5013F43F7B6ED52B4539FA16DC77957AEE815B9C07004C7E992EB8C7E591964AFEEA2",
+ "84264A73A818C95C2F424B37D3CC990B046FB50C2DC64A164211889A010F2471A0912FFEA1BF0195"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(440),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "D67B8C1734F46FA3F763CF57C6F9F4F2" +
+ "DC1089BD8BC1F6F023950BFC5617635208C8501238AD7A44" +
+ "00DEFEE46C640B61AF77C2D1A3BFAA90EDE5D207406E5403",
+ "8FDAEC20F8B421407059E3588920DA7E" +
+ "DA9DCE3CF8274DFA1C59C108C1D0AA9B0FA38DA5C792037C" +
+ "4D33CD070CA7CD0C5608DBA8B885654639DE2187B74CB263"
+ }),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(440),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "FABD0AE25C69DC2EFDEFB7F20C5A31B5" +
+ "7AC938AB771AA19BF8F5F1468F665C938C9A1A5DF0628A56" +
+ "90F15A1AD8A613F31BBD65EEAD5457D5D26947F29FE91AA7",
+ "6BD925B0E1C232EFD67CCD84F722E927" +
+ "ECB46AB2B740014777AF14BA0BBF53A45BDBB62B3F7D0B9C" +
+ "8EEAD057C0EC754EF8B53E60A1F434F05946A8B686AFBC7A"
+ }),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]{
+ "03AB8BCE4D1DBBB636C5C5B7E1C58499FEB1C619CDD11D35" +
+ "CD6CF6BB8F20EF27B6F5F9054FF900DB9EBF7BF30ED4DCBB" +
+ "BC8D5B51C965EA226FFEE2CA5AB2EFD00754DC32F357BF7A" +
+ "E42275E0F7704DC44E50A5220AD05AB698A22640AC634829",
+ "B907E77144FD55A54E9BA1A6A0EED0AAC780020C41A15DD8" +
+ "9A6C163830BA1D094E6A17100FF71EE30A96E1EE04D2A966" +
+ "03832A4E404F1966C2B5F4CB61B9927E8D12AC1E1A24CF23" +
+ "88C14E8EC96C35181EAEE32AAA46330DEAAFE5E7CE783C74"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]{
+ "804A3AD720F4FCE8738D0632514FEF16430CB7D63A8DF1A5" +
+ "F02A3CE3BD7ED6A668B69E63E2BB93F096EE753D6194A0F1" +
+ "A32711063653009636337D22167CC4402D019AC216FA574F" +
+ "091CF6EA283568D737A77BE38E8F09382C69E76B142ABC3A",
+ "73B8E55C753202176A17B9B9754A9FE6F23B01861FCD4059" +
+ "6AEAA301AF1AEF8AF0EAF22FBF34541EFFAB1431666ACACC" +
+ "759338C7E28672819D53CFEF10A3E19DAFBD53295F1980A9" +
+ "F491504A2725506784B7AC826D92C838A8668171CAAA86E7"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "2A5FF6520C20F66E" +
+ "D5EA431BD4AEAC58F975EEC9A015137D5C94B73AA09CB8B5" +
+ "9D611DDEECEB34A52BB999424009EB9EAC5353F92A6699D2" +
+ "0A02164EEBBC6492941E10426323898465DFD731C7E04730" +
+ "60A5AA8973841FDF3446FB6E72A58DA8BDA2A57A36F3DD98" +
+ "6DF85C8A5C6FF31CDE660BF8A841B21DD6AA9D3AC356B87B",
+ "0EDC8D7D7CEEC7FE" +
+ "36333FB30C0A9A4B27AA0BECBF075568B006C1C3693B1C29" +
+ "0F84769C213F98EB5880909EDF068FDA6BFC43503987BBBD" +
+ "4FC23AFBE982FE4B4B007910CC4874EEC217405421C8D8A1" +
+ "BA87EC684D0AF9A6101D9DB787AE82C3A6A25ED478DF1B12" +
+ "212CEC325466F3AC7C48A56166DD0B119C8673A1A9D54F67"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "AAE4DC3C9ECC74D9" +
+ "061DD527117EF3D29E1E52B26853C539D6CA797E8DA3D0BB" +
+ "171D8E30B8B194D8C28F7F6BE3B986B88506DC6A01B294A7" +
+ "165DD1C3470F7BE7B396AA0DB7D50C4051E7C7E1C8A7D21A" +
+ "2B5878C0BCB163CAA79366E7A1162FDC88429616CD3E6977" +
+ "8D327520A6BBBF71D8AA2E03EC4A9DAA0E77CF93E1EE30D2 ",
+ "129FF6D31A23FFBC" +
+ "870632B35EE477C2280DDD2ECDABEDB900C78418BE2D243B" +
+ "B9D8E5093ECE7B6BF48638D8F704D134ADDEB7F4E9D5C142" +
+ "CD05683E72B516486AF24AEC15D61E81E270DD4EBED91B62" +
+ "12EB8896A6250D5C8BC3A4A12F7E3068FBDF856F47EB23D3" +
+ "79F82C1EBCD1585FB260B9C0C42625FBCEE68CAD773CD5B1"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "7AE31A2DEC31075F" +
+ "E5972660C16D22ECC0D415C5693001BE5A468B590BC1AE2C" +
+ "43F647F8D681AEEA0D87B79B0B4E5D089CA2C9D327534234" +
+ "0254E6B04690D77A71A294DA9568479EEF8BB2A2110F18B6" +
+ "22F60F35235DE0E8F9D7E98105D84AA24AF0757AF005DFD5" +
+ "2FA51DE3F44FCE0C5F3A27FCE8B0F6E4A3F7C7B53CE34A3D",
+ "D83A8084630F286D" +
+ "A4DB49B9F6F608C8993F7F1397EA0D6F4A72CF3EF2733A11" +
+ "AB823C29F2EBDEC3EDE962F93D920A1DB59C84E1E879C29F" +
+ "5F9995FC3A6A3AF9B587CA7C13EA197D423E81E1D6469942" +
+ "B6E2CA83A97E91F6B298266AC148A1809776C26AF5E239A5" +
+ "5A2BEB9E752203A694E1F3FE2B3E6A0C9C314421CDB55FBD "})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE")
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "28FD6060C4F35F4D" +
+ "317AB2060EE32019E0DAA330F3F5650BBCA57CB67EE6AF1C" +
+ "6F25D1B01F3601EDA85DC2ED29A9B2BA4C85CF491CE7185F" +
+ "1A2BD9378AE3C655BD1CEC2EE108AE7FC382989F6D4FEA8A" +
+ "B01499697C2F07945CE02C5ED617D04287FEAF3BA638A4CE" +
+ "F3BB6B827E40AF16279580FCF1FDAD830930F7FDE341E2AF",
+ "C0B1601AFE39338B" +
+ "58DC2BE7C256AEBE3C21C5A939BEEC7E97B3528AC420F0C6" +
+ "341847187666E0FF578A8EB0A37809F877365A28DF2FA0F0" +
+ "6354A6F02496747369375B9A9D6B756FDC4A8FB308E08256" +
+ "9D79A85BB960F747256626389A3B45B0ABE7ECBC39D5CD7B" +
+ "2C18DF2E5FDE8C9B8D43474C54B6F9839468445929B438C7"}),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "72691D2103FB567C" +
+ "CD30370715B36666F63430087B1C688281CA0974DB456BDB" +
+ "A7EB5C48CFF62EA05F9508F3B530CE995A272B11EC079C13" +
+ "923EEF8E011A93C19B58CC6716BC7CB8BD886CAA60C14D85" +
+ "C023348BD77738C475D6C7E1D9BFF4B12C43D8CC73F838DC" +
+ "4F8BD476CF8328EEB71B3D873D6B7B859C9B21065638FF95",
+ "8570DA3D47E1E160" +
+ "5CF3E44B8D328B995EFC64107B6292D1B1036B5F88CE3160" +
+ "2F12BEB71D801C0942E7C0864B3DB67A9356DB203490D881" +
+ "24FE86BCE38AC2269B4FDA6ABAA884039DF80A0336A24D79" +
+ "1EB3067C8F5F0CF0F18DD73B66A7B316FB19E02835CC6293" +
+ "65FCD1D3BE640178ED9093B91B36E1D68135F2785BFF505C"})
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "AAE4DC3C9ECC74D9" +
+ "061DD527117EF3D29E1E52B26853C539D6CA797E8DA3D0BB" +
+ "171D8E30B8B194D8C28F7F6BE3B986B88506DC6A01B294A7" +
+ "165DD1C3470F7BE7B396AA0DB7D50C4051E7C7E1C8A7D21A" +
+ "2B5878C0BCB163CAA79366E7A1162FDC88429616CD3E6977" +
+ "8D327520A6BBBF71D8AA2E03EC4A9DAA0E77CF93E1EE30D2 ",
+ "129FF6D31A23FFBC" +
+ "870632B35EE477C2280DDD2ECDABEDB900C78418BE2D243B" +
+ "B9D8E5093ECE7B6BF48638D8F704D134ADDEB7F4E9D5C142" +
+ "CD05683E72B516486AF24AEC15D61E81E270DD4EBED91B62" +
+ "12EB8896A6250D5C8BC3A4A12F7E3068FBDF856F47EB23D3" +
+ "79F82C1EBCD1585FB260B9C0C42625FBCEE68CAD773CD5B1"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]{
+ "B8E827652175E6E0" +
+ "6E513C7BE94B5810C14ED94AD903647940CAEB7EE014C848" +
+ "8DCBBE6D4D6616D06656A3DC707CDAC4F02EE6D8408C065F" +
+ "CB068C0760DA47C5D60E5D70D09DC3929B6979615D117F7B" +
+ "EDCC661A98514B3A1F55B2CBABDCA59F11823E4838065F1F" +
+ "8431CBF28A577738234AF3F188C7190CC19739E72E9BBFFF",
+ "7ED41B9CFDC8C256" +
+ "83BBB4C553CC2DC61F690E62ABC9F038A16B8C519690CABE" +
+ "BD1B5C196C57CF759BB9871BE0C163A57315EA96F615136D" +
+ "064572F09F26D659D24211F9610FFCDFFDA8CE23FFA96735" +
+ "7595182660877766035EED800B05364CE324A75EB63FD9B3" +
+ "EED956D147480B1D0A42DF8AA990BB628666F6F61D60CBE2"})
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE")
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E")
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DRBGTestVector[] tests = createTestVectorData();
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ DRBGTestVector tv = tests[i];
+
+ byte[] nonce = tv.nonce();
+ byte[] personalisationString = tv.personalizationString();
+
+ SP80090DRBG d = new HMacSP800DRBG(new HMac(tv.getDigest()), tv.securityStrength(), tv.entropySource(), personalisationString, nonce);
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output)));
+ }
+
+ output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(1), tv.predictionResistance());
+
+ expected = tv.expectedValue(1);
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output)));
+ }
+ }
+
+ // Exception tests
+ //
+ SP80090DRBG d;
+ try
+ {
+ d = new HMacSP800DRBG(new HMac(new SHA256Digest()), 256, new SHA256EntropyProvider().get(128), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Not enough entropy for security strength required"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+ }
+
+ private class SHA1EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA1EntropyProvider()
+ {
+ super(
+ Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536"
+ + "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6"
+ + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ private class SHA256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA256EntropyProvider()
+ {
+ super(Hex.decode(
+ "00010203040506" +
+ "0708090A0B0C0D0E0F101112131415161718191A1B1C1D1E" +
+ "1F202122232425262728292A2B2C2D2E2F30313233343536" +
+ "80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "C0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ private class SHA384EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA384EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526"
+ + "2728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556"
+ + "5758595A5B5C5D5E5F606162636465666768696A6B6C6D6E" +
+ "808182838485868788898A8B8C8D8E" +
+ "8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6" +
+ "A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE" +
+ "BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6" +
+ "D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCE" +
+ "CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6" +
+ "E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE" +
+ "FF000102030405060708090A0B0C0D0E0F10111213141516" +
+ "1718191A1B1C1D1E1F202122232425262728292A2B2C2D2E"), true);
+ }
+ }
+
+ private class SHA512EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA512EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E" +
+ "0F101112131415161718191A1B1C1D1E1F20212223242526" +
+ "2728292A2B2C2D2E2F303132333435363738393A3B3C3D3E" +
+ "3F404142434445464748494A4B4C4D4E4F50515253545556" +
+ "5758595A5B5C5D5E5F606162636465666768696A6B6C6D6E" +
+ "808182838485868788898A8B8C8D8E" +
+ "8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6" +
+ "A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE" +
+ "BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6" +
+ "D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCE" +
+ "CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6" +
+ "E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE" +
+ "FF000102030405060708090A0B0C0D0E0F10111213141516" +
+ "1718191A1B1C1D1E1F202122232425262728292A2B2C2D2E"), true);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/HashDRBGTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/HashDRBGTest.java
new file mode 100644
index 00000000..ee632033
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/HashDRBGTest.java
@@ -0,0 +1,481 @@
+package org.bouncycastle.crypto.prng.test;
+
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG;
+import org.bouncycastle.crypto.prng.drbg.SP80090DRBG;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * DRBG Test
+ */
+public class HashDRBGTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "HashDRBG";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HashDRBGTest());
+ }
+
+ private DRBGTestVector[] createTestVectorData()
+ {
+ return new DRBGTestVector[]
+ {
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "9F7CFF1ECA23E750F66326969F11800F12088BA68E441D15D888B3FE12BF66FE057494F4546DE2F1",
+ "B77AA5C0CD55BBCEED7574AF223AFD988C7EEC8EFF4A94E5E89D26A04F58FA79F5E0D3702D7A9A6A"
+ }
+ ),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "AB438BD3B01A0AF85CFEE29F7D7B71621C4908B909124D430E7B406FB1086EA994C582E0D656D989",
+ "29D9098F987E7005314A0F51B3DD2B8122F4AED706735DE6AD5DDBF223177C1E5F3AEBC52FAB90B9"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "E76B4EDD5C865BC8AFD809A59B69B429AC7F4352A579BCF3F75E56249A3491F87C3CA6848B0FAB25",
+ "6577B6B4F87A93240B199FE51A3B335313683103DECE171E3256FB7E803586CA4E45DD242EB01F70"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "56EF4913373994D5539F4D7D17AFE7448CDF5E72416CC6A71A340059FA0D5AE526B23250C46C0944",
+ "575B37A2739814F966C63B60A2C4F149CA9ACC84FC4B25493289B085C67B2E30F5F0B99A2C349E2A"
+ }),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "532CA1165DCFF21C55592687639884AF4BC4B057DF8F41DE653AB44E2ADEC7C9303E75ABE277EDBF",
+ "73C2C67C696D686D0C4DBCEB5C2AF7DDF6F020B6874FAE4390F102117ECAAFF54418529A367005A0"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576"),
+ new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "183C242A1430E46C4ED70B4DBE1BF9AB0AB8721CDCA2A2D1820AD6F6C956858543B2AA191D8D1287",
+ "F196F9BD021C745CBD5AC7BFCE48EAAF0D0E7C091FBF436940E63A198EE770D9A4F0718669AF2BC9"
+ })
+ .addAdditionalInput("606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F90919293949596")
+ .addAdditionalInput("A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6"),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(440),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "77E05A0E7DC78AB5D8934D5E93E82C06" +
+ "A07C04CEE6C9C53045EEB485872777CF3B3E35C474F976B8" +
+ "94BF301A86FA651F463970E89D4A0534B2ECAD29EC044E7E",
+ "5FF4BA493C40CFFF3B01E472C575668C" +
+ "CE3880B9290B05BFEDE5EC96ED5E9B2898508B09BC800EEE" +
+ "099A3C90602ABD4B1D4F343D497C6055C87BB956D53BF351"
+ }
+ ),
+ new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(440),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "92275523C70E567BCF9B35EC50B933F8" +
+ "12616DF586B7F72EE1BC7735A5C2654373CBBC72316DFF84" +
+ "20A33BF02B97AC8D1952583F270ACD7005CC027F4CF1187E",
+ "681A46B2AA8694A0FE4DEEA720927A84" +
+ "EAAA985E59C19F8BE0984D8CBEF8C69B754167641946E040" +
+ "EE2043E1CCB29DCF063C0A50830E428E6DCA262ECD77C542"
+ }),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "04FF23AD15E78790ADD36B438BBC097C7A11747CC2CCEEDE" +
+ "2C978B23B3DC63B732C953061D7764990ABFEFC47A581B92" +
+ "1BC0428C4F12212460E406A0F0651E7F0CB9A90ABFDB07B5" +
+ "25565C74F0AA085082F6CF213AAFAD0C0646895078F1E1FE",
+ "4F35B85F95DEE3E873054905CFD02341653E18F529930CBE" +
+ "14D909F37FEAF2C790D22FAE7516B4590BE35D53E2FE1A35" +
+ "AFE4B6607CB358589C3B4D094A1D81FE0717F1DF5BDDEB3E" +
+ "114F130BB781E66C22B5B770E8AE115FF39F8ADAF66DEEDF"
+ }
+ ),
+ new DRBGTestVector(
+ new SHA384Digest(),
+ new SHA384EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B",
+ 192,
+ new String[]
+ {
+ "97993B78F7C31C0E876DC92EB7D6C408E09D608AD6B99D0E" +
+ "A2229B05A578C426334FCC8A1C7E676ED2D89A5B4CDF5B3F" +
+ "4ADF11936BF14F4E10909DBA9C24F4FDFFDE72351DA8E2CC" +
+ "3B135A395373899E5F1A5955B880CA9B9E9DD4C9CA7FA4D4",
+ "F5983946320E36C64EF283CA1F65D197CF81624EC6778E77" +
+ "0E78949D84EF21A45CDD62D1DB76920D4C2836FC6AE5299F" +
+ "AF1357D9701FAD10FBD88D1E2832239436D76EB271BDC3CA" +
+ "04425EC88BC0E89A4D5C37FFCE7C6C3ABDE9C413AE6D3FEA"
+ }
+ ),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ false,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "DA126CF95C6BF97E" +
+ "2F731F2137A907ACC70FD7AC9EBACD1C6E31C74029B052E3" +
+ "AABC48F3B00993F2B2381F7650A55322A968C86E05DE88E6" +
+ "367F6EF89A601DB4342E9086C7AC13B5E56C32E9E668040B" +
+ "73847893C5BFD38A1CF44F348B4EEE4CD68ADB7E7B8C837F" +
+ "19BC4F902761F7CFF24AB1D704FD11C4E929D8553753B55D",
+ "400B977CE8A2BB6A" +
+ "84C6FD1CF901459685ABF5408CFF4588CEDF52E2D2DC300A" +
+ "A9B4FAED8CD0161C2172B1FD269253195883D6EBF21020F2" +
+ "C20E5F2C81AE60C8595B834A229B1F5B726C1125717E6207" +
+ "8886EF38E61E32707AD5F8116C6393DFB6E7C7AE0E8E92BB" +
+ "D7E0C3D04BBA02F5169F2F569A58158915FEE4C9D28D45DB"
+ }
+ )
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE")
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "F93CA6855590A77F" +
+ "07354097E90E026648B6115DF008FFEDBD9D9811F54E8286" +
+ "EF00FDD6BA1E58DF2535E3FBDD9A9BA3754A97F36EE83322" +
+ "1582060A1F37FCE4EE8826636B28EAD589593F4CA8B64738" +
+ "8F24EB3F0A34796968D21BDEE6F81FD5DF93536F935937B8" +
+ "025EC8CBF57DDB0C61F2E41463CC1516D657DA2829C6BF90",
+ "4817618F48C60FB1" +
+ "CE5BFBDA0CAF4591882A31F6EE3FE0F78779992A06EC60F3" +
+ "7FB9A8D6108C231F0A927754B0599FA4FA27A4E25E065EF0" +
+ "3085B892979DC0E7A1080883CAEBFDFD3665A8F2D061C521" +
+ "F7D6E3DA2AF8B97B6B43B6EC831AF515070A83BBB9AC95ED" +
+ "4EF49B756A2377A5F0833D847E27A88DDB0C2CE4AD782E7B "
+ }
+ ),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "0455DD4AD7DBACB2" +
+ "410BE58DF7248D765A4547ABAEE1743B0BCAD37EBD06DA7C" +
+ "F7CE5E2216E525327E9E2005EBEF2CE53BD733B18128627D" +
+ "3FD6153089373AF2606A1584646A0EA488BFEF45228699A0" +
+ "89CEA8AEC44502D86D9591F3552C688B7F7B45FCB0C3C2B9" +
+ "43C1CD8A6FC63DF4D81C3DA543C9CF2843855EA84E4F959C",
+ "C047D46D7F614E4E" +
+ "4A7952C79A451F8F7ACA379967E2977C401C626A2ED70D74" +
+ "A63660579A354115BC8C8C8CC3AEA3050686A0CFCDB6FA9C" +
+ "F78D4C2165BAF851C6F9B1CD16A2E14C15C6DAAC56C16E75" +
+ "FC84A14D58B41622E88B0F1B1995587FD8BAA999CBA98025" +
+ "4C8AB9A9691DF7B84D88B639A9A3106DEABEB63748B99C09"
+ }
+ )
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "22EB93A67911DA73" +
+ "85D9180C78127DE1A04FF713114C07C9C615F7CC5EF72744" +
+ "A2DDCD7C3CB85E65DED8EF5F240FBDCBEBBDE2BAAC8ECF7D" +
+ "CBC8AC333E54607AD41DC495D83DF72A05EF55B127C1441C" +
+ "9A0EFFDA2C7954DB6C2D04342EB812E5E0B11D6C395F41ED" +
+ "A2702ECE5BA479E2DFA18F953097492636C12FE30CE5C968",
+ "E66698CFBF1B3F2E" +
+ "919C03036E584EAA81CF1C6666240AF05F70637043733954" +
+ "D8A1E5A66A04C53C6900FDC145D4A3A80A31F5868ACE9AC9" +
+ "4E14E2051F624A05EEA1F8B684AA5410BCE315E76EA07C71" +
+ "5D6F34731320FF0DCF78D795E6EFA2DF92B98BE636CDFBA2" +
+ "9008DD392112AEC202F2E481CB9D83F987FEA69CD1B368BB"
+ }
+ )
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE"),
+ new DRBGTestVector(
+ new SHA512Digest(),
+ new SHA512EntropyProvider().get(888),
+ true,
+ "202122232425262728292A2B2C2D2E2F",
+ 256,
+ new String[]
+ {
+ "7596A76372308BD5" +
+ "A5613439934678B35521A94D81ABFE63A21ACF61ABB88B61" +
+ "E86A12C37F308F2BBBE32BE4B38D03AE808386494D70EF52" +
+ "E9E1365DD18B7784CAB826F31D47579E4D57F69D8BF3152B" +
+ "95741946CEBE58571DF58ED39980D9AF44E69F01E8989759" +
+ "8E40171101A0E3302838E0AD9E849C01988993CF9F6E5263",
+ "DBE5EE36FCD85301" +
+ "303E1C3617C1AC5E23C08885D0BEFAAD0C85A0D89F85B9F1" +
+ "6ECE3D88A24EB96504F2F13EFA7049621782F5DE2C416A0D" +
+ "294CCFE53545C4E309C48E1E285A2B829A574B72B3C2FBE1" +
+ "34D01E3706B486F2401B9820E17298A342666918E15B8462" +
+ "87F8C5AF2D96B20FAF3D0BB392E15F4A06CDB0DECD1B6AD7"
+ }
+ )
+ .setPersonalizationString(
+ "404142434445464748494A4B4C4D4E" +
+ "4F505152535455565758595A5B5C5D5E5F60616263646566" +
+ "6768696A6B6C6D6E6F707172737475767778797A7B7C7D7E" +
+ "7F808182838485868788898A8B8C8D8E8F90919293949596" +
+ "9798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAE")
+ .addAdditionalInput(
+ "606162636465666768696A6B6C6D6E" +
+ "6F707172737475767778797A7B7C7D7E7F80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCE")
+ .addAdditionalInput(
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAE" +
+ "AFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6" +
+ "F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E")
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ DRBGTestVector[] tests = createTestVectorData();
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ DRBGTestVector tv = tests[i];
+
+ byte[] nonce = tv.nonce();
+ byte[] personalisationString = tv.personalizationString();
+
+ SP80090DRBG d = new HashSP800DRBG(tv.getDigest(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce);
+
+ byte[] output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(0), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".1 failed, expected " + new String(Hex.encode(tv.expectedValue(0))) + " got " + new String(Hex.encode(output)));
+ }
+
+ output = new byte[tv.expectedValue(0).length];
+
+ d.generate(output, tv.additionalInput(1), tv.predictionResistance());
+
+ expected = tv.expectedValue(1);
+ if (!areEqual(expected, output))
+ {
+ fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output)));
+ }
+ }
+
+ // Exception tests
+ //
+ SP80090DRBG d;
+ try
+ {
+ d = new HashSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(128), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Not enough entropy for security strength required"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+
+ try
+ {
+ d = new HashSP800DRBG(new SHA1Digest(), 256, new SHA256EntropyProvider().get(256), null, null);
+ fail("no exception thrown");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("Requested security strength is not supported by the derivation function"))
+ {
+ fail("Wrong exception", e);
+ }
+ }
+ }
+
+ private class SHA1EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA1EntropyProvider()
+ {
+ super(
+ Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536"
+ + "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6"
+ + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ private class SHA256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA256EntropyProvider()
+ {
+ super(Hex.decode(
+ "00010203040506" +
+ "0708090A0B0C0D0E0F101112131415161718191A1B1C1D1E" +
+ "1F202122232425262728292A2B2C2D2E2F30313233343536" +
+ "80818283848586" +
+ "8788898A8B8C8D8E8F909192939495969798999A9B9C9D9E" +
+ "9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6" +
+ "C0C1C2C3C4C5C6" +
+ "C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDE" +
+ "DFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ private class SHA384EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA384EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223242526"
+ + "2728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F50515253545556"
+ + "5758595A5B5C5D5E5F606162636465666768696A6B6C6D6E" +
+ "808182838485868788898A8B8C8D8E" +
+ "8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6" +
+ "A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE" +
+ "BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6" +
+ "D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCE" +
+ "CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6" +
+ "E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE" +
+ "FF000102030405060708090A0B0C0D0E0F10111213141516" +
+ "1718191A1B1C1D1E1F202122232425262728292A2B2C2D2E"), true);
+ }
+ }
+
+ private class SHA512EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA512EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E" +
+ "0F101112131415161718191A1B1C1D1E1F20212223242526" +
+ "2728292A2B2C2D2E2F303132333435363738393A3B3C3D3E" +
+ "3F404142434445464748494A4B4C4D4E4F50515253545556" +
+ "5758595A5B5C5D5E5F606162636465666768696A6B6C6D6E" +
+ "808182838485868788898A8B8C8D8E" +
+ "8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6" +
+ "A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBE" +
+ "BFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6" +
+ "D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEE" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCE" +
+ "CFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6" +
+ "E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFE" +
+ "FF000102030405060708090A0B0C0D0E0F10111213141516" +
+ "1718191A1B1C1D1E1F202122232425262728292A2B2C2D2E"), true);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/RegressionTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/RegressionTest.java
new file mode 100644
index 00000000..9b03637f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/RegressionTest.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.crypto.prng.test;
+
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+public class RegressionTest
+{
+ public static Test[] tests = {
+ new CTRDRBGTest(),
+ new DualECDRBGTest(),
+ new HashDRBGTest(),
+ new HMacDRBGTest(),
+ new SP800RandomTest(),
+ new X931Test(),
+ new FixedSecureRandomTest()
+ };
+
+ public static void main(
+ String[] args)
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult result = tests[i].perform();
+
+ if (result.getException() != null)
+ {
+ result.getException().printStackTrace();
+ }
+
+ System.out.println(result);
+ }
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/SP800RandomTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/SP800RandomTest.java
new file mode 100644
index 00000000..a164f8fc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/SP800RandomTest.java
@@ -0,0 +1,319 @@
+package org.bouncycastle.crypto.prng.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class SP800RandomTest
+ extends SimpleTest
+{
+
+ public String getName()
+ {
+ return "SP800RandomTest";
+ }
+
+ private void testHashRandom()
+ {
+ DRBGTestVector tv = new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "532CA1165DCFF21C55592687639884AF4BC4B057DF8F41DE653AB44E2ADEC7C9303E75ABE277EDBF",
+ "73C2C67C696D686D0C4DBCEB5C2AF7DDF6F020B6874FAE4390F102117ECAAFF54418529A367005A0"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576");
+
+ doHashTest(0, tv);
+
+ tv = new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "AB438BD3B01A0AF85CFEE29F7D7B71621C4908B909124D430E7B406FB1086EA994C582E0D656D989",
+ "29D9098F987E7005314A0F51B3DD2B8122F4AED706735DE6AD5DDBF223177C1E5F3AEBC52FAB90B9"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576");
+
+ doHashTest(1, tv);
+ }
+
+ private void doHashTest(int index, DRBGTestVector tv)
+ {
+ SP800SecureRandomBuilder rBuild = new SP800SecureRandomBuilder(new SHA1EntropyProvider());
+
+ rBuild.setPersonalizationString(tv.personalizationString());
+ rBuild.setSecurityStrength(tv.securityStrength());
+ rBuild.setEntropyBitsRequired(tv.entropySource().getEntropy().length * 8);
+
+ SecureRandom random = rBuild.buildHash(tv.getDigest(), tv.nonce(), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+ byte[] produced = new byte[expected.length];
+
+ random.nextBytes(produced);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail(index + " SP800 Hash SecureRandom produced incorrect result (1)");
+ }
+
+ random.nextBytes(produced);
+ expected = tv.expectedValue(1);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail(index + " SP800 Hash SecureRandom produced incorrect result (2)");
+ }
+ }
+
+ private void testHMACRandom()
+ {
+ DRBGTestVector tv = new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ true,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "6C37FDD729AA40F80BC6AB08CA7CC649794F6998B57081E4220F22C5C283E2C91B8E305AB869C625",
+ "CAF57DCFEA393B9236BF691FA456FEA7FDF1DF8361482CA54D5FA723F4C88B4FA504BF03277FA783"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576");
+
+ doHMACTest(tv);
+
+ tv = new DRBGTestVector(
+ new SHA1Digest(),
+ new SHA1EntropyProvider().get(440),
+ false,
+ "2021222324",
+ 80,
+ new String[]
+ {
+ "5A7D3B449F481CB38DF79AD2B1FCC01E57F8135E8C0B22CD0630BFB0127FB5408C8EFC17A929896E",
+ "82cf772ec3e84b00fc74f5df104efbfb2428554e9ce367d03aeade37827fa8e9cb6a08196115d948"
+ });
+
+ doHMACTest(tv);
+ }
+
+ private void doHMACTest(DRBGTestVector tv)
+ {
+ SP800SecureRandomBuilder rBuild = new SP800SecureRandomBuilder(new SHA1EntropyProvider());
+
+ rBuild.setPersonalizationString(tv.personalizationString());
+ rBuild.setSecurityStrength(tv.securityStrength());
+ rBuild.setEntropyBitsRequired(tv.entropySource().getEntropy().length * 8);
+
+ SecureRandom random = rBuild.buildHMAC(new HMac(tv.getDigest()), tv.nonce(), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+ byte[] produced = new byte[expected.length];
+
+ random.nextBytes(produced);
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail("SP800 HMAC SecureRandom produced incorrect result (1)");
+ }
+
+ random.nextBytes(produced);
+ expected = tv.expectedValue(1);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail("SP800 HMAC SecureRandom produced incorrect result (2)");
+ }
+ }
+
+ private void testDualECRandom()
+ {
+ DRBGTestVector tv = new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ false,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "3AB095CC493A8730D70DE923108B2E4710799044FFC27D0A1156250DDF97E8B05ACE055E49F3E3F5B928CCD18317A3E68FCB0B6F0459ADF9ECF79C87",
+ "7B902FC35B0AF50F57F8822936D08A96E41B16967C6B1AA0BC05032F0D53919DC587B664C883E2FE8F3948002FCD8BCBFC4706BCAA2075EF6BF41167"
+ })
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F");
+
+ doDualECTest(1, tv);
+
+ tv = new DRBGTestVector(
+ new SHA256Digest(),
+ new SHA256EntropyProvider().get(128),
+ true,
+ "2021222324252627",
+ 128,
+ new String[]
+ {
+ "8C77288EDBEA9A742464F78D55E33593C1BF5F9D8CD8609D6D53BAC4E4B42252A227A99BAD0F2358B05955CD35723B549401C71C9C1F32F8A2018E24",
+ "56ECA61C64F69C1C232E992623C71418BD0B96D783118FAAD94A09E3A9DB74D15E805BA7F14625995CA77612B2EF7A05863699ECBABF70D3D422C014"
+ });
+
+ doDualECTest(2, tv);
+ }
+
+ private void doDualECTest(int index, DRBGTestVector tv)
+ {
+ SP800SecureRandomBuilder rBuild = new SP800SecureRandomBuilder(new SHA256EntropyProvider());
+
+ rBuild.setPersonalizationString(tv.personalizationString());
+ rBuild.setSecurityStrength(tv.securityStrength());
+ rBuild.setEntropyBitsRequired(tv.securityStrength());
+
+ SecureRandom random = rBuild.buildDualEC(tv.getDigest(), tv.nonce(), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+ byte[] produced = new byte[expected.length];
+
+ random.nextBytes(produced);
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail(index + " SP800 Dual EC SecureRandom produced incorrect result (1)");
+ }
+
+ random.nextBytes(produced);
+ expected = tv.expectedValue(1);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail(index + " SP800 Dual EC SecureRandom produced incorrect result (2)");
+ }
+ }
+
+ private void testCTRRandom()
+ {
+ DRBGTestVector tv = new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ false,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "ABC88224514D0316EA3D48AEE3C9A2B4",
+ "D3D3F372E43E7ABDC4FA293743EED076"
+ }
+ );
+
+ doCTRTest(tv);
+
+ tv = new DRBGTestVector(
+ new DESedeEngine(), 168,
+ new Bit232EntropyProvider().get(232),
+ true,
+ "20212223242526",
+ 112,
+ new String[]
+ {
+ "64983055D014550B39DE699E43130B64",
+ "035FDDA8582A2214EC722C410A8D95D3"
+ }
+ )
+ .setPersonalizationString("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C");
+
+ doCTRTest(tv);
+ }
+
+ private void doCTRTest(DRBGTestVector tv)
+ {
+ SP800SecureRandomBuilder rBuild = new SP800SecureRandomBuilder(new Bit232EntropyProvider());
+
+ rBuild.setPersonalizationString(tv.personalizationString());
+ rBuild.setSecurityStrength(tv.securityStrength());
+ rBuild.setEntropyBitsRequired(tv.entropySource().getEntropy().length * 8);
+
+ SecureRandom random = rBuild.buildCTR(tv.getCipher(), tv.keySizeInBits(), tv.nonce(), tv.predictionResistance());
+
+ byte[] expected = tv.expectedValue(0);
+ byte[] produced = new byte[expected.length];
+
+ random.nextBytes(produced);
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail("SP800 CTR SecureRandom produced incorrect result (1)");
+ }
+
+ random.nextBytes(produced);
+ expected = tv.expectedValue(1);
+
+ if (!Arrays.areEqual(expected, produced))
+ {
+ fail("SP800 CTR SecureRandom produced incorrect result (2)");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testHashRandom();
+ testHMACRandom();
+ testCTRRandom();
+ testDualECRandom();
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SP800RandomTest());
+ }
+
+ // for HMAC/Hash
+ private class SHA1EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA1EntropyProvider()
+ {
+ super(
+ Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233343536"
+ + "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6"
+ + "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6"), true);
+ }
+ }
+
+ // for Dual EC
+ private class SHA256EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ SHA256EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F " +
+ "808182838485868788898A8B8C8D8E8F" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"), true);
+ }
+ }
+
+ private class Bit232EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ Bit232EntropyProvider()
+ {
+ super(Hex.decode(
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C" +
+ "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C" +
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDC"), true);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/TestEntropySourceProvider.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/TestEntropySourceProvider.java
new file mode 100644
index 00000000..64e75958
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/TestEntropySourceProvider.java
@@ -0,0 +1,46 @@
+package org.bouncycastle.crypto.prng.test;
+
+import org.bouncycastle.crypto.prng.EntropySource;
+import org.bouncycastle.crypto.prng.EntropySourceProvider;
+
+public class TestEntropySourceProvider
+ implements EntropySourceProvider
+{
+ private final byte[] data;
+ private final boolean isPredictionResistant;
+
+ protected TestEntropySourceProvider(byte[] data, boolean isPredictionResistant)
+ {
+ this.data = data;
+ this.isPredictionResistant = isPredictionResistant;
+ }
+
+ public EntropySource get(final int bitsRequired)
+ {
+ return new EntropySource()
+ {
+ int index = 0;
+
+ public boolean isPredictionResistant()
+ {
+ return isPredictionResistant;
+ }
+
+ public byte[] getEntropy()
+ {
+ byte[] rv = new byte[bitsRequired / 8];
+
+ System.arraycopy(data, index, rv, 0, rv.length);
+
+ index += bitsRequired / 8;
+
+ return rv;
+ }
+
+ public int entropySize()
+ {
+ return bitsRequired;
+ }
+ };
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/X931Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/X931Test.java
new file mode 100644
index 00000000..26ed67f0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/X931Test.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.crypto.prng.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.prng.X931SecureRandomBuilder;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * HMAC SP800-90 DRBG
+ */
+public class X931Test
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "X931";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new X931Test());
+ }
+
+ private X931TestVector[] createTestVectorData()
+ {
+ return new X931TestVector[]
+ {
+ new X931TestVector(
+ new AESEngine(),
+ new AES128EntropyProvider(),
+ "f7d36762b9915f1ed585eb8e91700eb2",
+ "259e67249288597a4d61e7c0e690afae",
+ false,
+ new String[] {
+ "15f013af5a8e9df9a8e37500edaeac43",
+ "a9d74bb1c90a222adc398546d64879cf",
+ "0379e404042d58180764fb9e6c5d94bb",
+ "3c74603e036d28c79947ffb56fee4e51",
+ "e872101a4df81ebbe1e632fc87195d52",
+ "26a6b3d33b8e7e68b75d9630ec036314" }),
+ new X931TestVector(
+ new DESedeEngine(),
+ new TDESEntropyProvider(),
+ "ef16ec643e5db5892cbc6eabba310b3410e6f8759e3e382c",
+ "55df103deaf68dc4",
+ false,
+ new String[] {
+ "9c960bb9662ce6de",
+ "d9d0e527fd0931da",
+ "3e2db9994e9e6995",
+ "0e3868aef8218cf7",
+ "7b0b0ca137f8fd81",
+ "f657df270ad12265" })
+ };
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ X931TestVector[] vectors = createTestVectorData();
+
+ for (int i = 0; i != vectors.length; i++)
+ {
+ X931TestVector tv = vectors[i];
+ X931SecureRandomBuilder bld = new X931SecureRandomBuilder(tv.getEntropyProvider());
+
+ bld.setDateTimeVector(Hex.decode(tv.getDateTimeVector()));
+
+ SecureRandom rand = bld.build(tv.getEngine(), new KeyParameter(Hex.decode(tv.getKey())), tv.isPredictionResistant());
+
+ for (int j = 0; j != tv.getExpected().length - 1; j++)
+ {
+ byte[] expected = Hex.decode(tv.getExpected()[j]);
+ byte[] res = new byte[expected.length];
+
+ rand.nextBytes(res);
+
+ if (!Arrays.areEqual(expected, res))
+ {
+ fail("expected output wrong [" + j + "] got : " + Strings.fromByteArray(Hex.encode(res)));
+ }
+ }
+
+ byte[] expected = Hex.decode(tv.getExpected()[tv.getExpected().length - 1]);
+ byte[] res = new byte[expected.length];
+
+ for (int j = tv.getExpected().length - 1; j != 10000; j++)
+ {
+ rand.nextBytes(res);
+ }
+
+ if (!Arrays.areEqual(expected, res))
+ {
+ fail("expected output wrong [" + 10000 + "] got : " + Strings.fromByteArray(Hex.encode(res)));
+ }
+ }
+ }
+
+ private class AES128EntropyProvider
+ extends TestEntropySourceProvider
+ {
+ AES128EntropyProvider()
+ {
+ super(Hex.decode(
+ "35cc0ea481fc8a4f5f05c7d4667233b2"), true);
+ }
+ }
+
+ private class TDESEntropyProvider
+ extends TestEntropySourceProvider
+ {
+ TDESEntropyProvider()
+ {
+ super(Hex.decode(
+ "96d872b9122c5e74"), true);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/X931TestVector.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/X931TestVector.java
new file mode 100644
index 00000000..ce57a965
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/test/X931TestVector.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.crypto.prng.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.prng.EntropySourceProvider;
+
+public class X931TestVector
+{
+ private final BlockCipher engine;
+ private final EntropySourceProvider entropyProvider;
+ private final String key;
+ private final String dateTimeVector;
+ private final boolean predictionResistant;
+ private final String[] expected;
+
+ public X931TestVector(BlockCipher engine, EntropySourceProvider entropyProvider, String key, String dateTimeVector, boolean predictionResistant, String[] expected)
+ {
+ this.engine = engine;
+ this.entropyProvider = entropyProvider;
+ this.key = key;
+
+
+ this.dateTimeVector = dateTimeVector;
+ this.predictionResistant = predictionResistant;
+ this.expected = expected;
+ }
+
+ public String getDateTimeVector()
+ {
+ return dateTimeVector;
+ }
+
+ public BlockCipher getEngine()
+ {
+ return engine;
+ }
+
+ public EntropySourceProvider getEntropyProvider()
+ {
+ return entropyProvider;
+ }
+
+ public String[] getExpected()
+ {
+ return expected;
+ }
+
+ public String getKey()
+ {
+ return key;
+ }
+
+ public boolean isPredictionResistant()
+ {
+ return predictionResistant;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java
index 2e4c48d3..684eb9c0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSADigestSigner.java
@@ -5,9 +5,9 @@ import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DSA;
@@ -142,8 +142,8 @@ public class DSADigestSigner
throws IOException
{
ASN1EncodableVector v = new ASN1EncodableVector();
- v.add(new DERInteger(r));
- v.add(new DERInteger(s));
+ v.add(new ASN1Integer(r));
+ v.add(new ASN1Integer(s));
return new DERSequence(v).getEncoded(ASN1Encoding.DER);
}
@@ -156,8 +156,8 @@ public class DSADigestSigner
return new BigInteger[]
{
- ((DERInteger)s.getObjectAt(0)).getValue(),
- ((DERInteger)s.getObjectAt(1)).getValue()
+ ((ASN1Integer)s.getObjectAt(0)).getValue(),
+ ((ASN1Integer)s.getObjectAt(1)).getValue()
};
}
}
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 292c4087..44f838b2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
@@ -45,18 +45,19 @@ public class DSASigner
boolean forSigning,
CipherParameters param)
{
+ SecureRandom providedRandom = null;
+
if (forSigning)
{
if (param instanceof ParametersWithRandom)
{
- ParametersWithRandom rParam = (ParametersWithRandom)param;
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
- this.random = rParam.getRandom();
this.key = (DSAPrivateKeyParameters)rParam.getParameters();
+ providedRandom = rParam.getRandom();
}
else
{
- this.random = new SecureRandom();
this.key = (DSAPrivateKeyParameters)param;
}
}
@@ -64,6 +65,8 @@ public class DSASigner
{
this.key = (DSAPublicKeyParameters)param;
}
+
+ this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
}
/**
@@ -77,32 +80,28 @@ public class DSASigner
byte[] message)
{
DSAParameters params = key.getParameters();
- BigInteger m = calculateE(params.getQ(), message);
+ BigInteger q = params.getQ();
+ BigInteger m = calculateE(q, message);
+ BigInteger x = ((DSAPrivateKeyParameters)key).getX();
if (kCalculator.isDeterministic())
{
- kCalculator.init(params.getQ(), ((DSAPrivateKeyParameters)key).getX(), message);
+ kCalculator.init(q, x, message);
}
else
{
- kCalculator.init(params.getQ(), random);
+ kCalculator.init(q, random);
}
BigInteger k = kCalculator.nextK();
- BigInteger r = params.getG().modPow(k, params.getP()).mod(params.getQ());
-
- k = k.modInverse(params.getQ()).multiply(
- m.add(((DSAPrivateKeyParameters)key).getX().multiply(r)));
-
- BigInteger s = k.mod(params.getQ());
+ BigInteger r = params.getG().modPow(k, params.getP()).mod(q);
- BigInteger[] res = new BigInteger[2];
+ k = k.modInverse(q).multiply(m.add(x.multiply(r)));
- res[0] = r;
- res[1] = s;
+ BigInteger s = k.mod(q);
- return res;
+ return new BigInteger[]{ r, s };
}
/**
@@ -116,28 +115,30 @@ public class DSASigner
BigInteger s)
{
DSAParameters params = key.getParameters();
- BigInteger m = calculateE(params.getQ(), message);
+ BigInteger q = params.getQ();
+ BigInteger m = calculateE(q, message);
BigInteger zero = BigInteger.valueOf(0);
- if (zero.compareTo(r) >= 0 || params.getQ().compareTo(r) <= 0)
+ if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0)
{
return false;
}
- if (zero.compareTo(s) >= 0 || params.getQ().compareTo(s) <= 0)
+ if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0)
{
return false;
}
- BigInteger w = s.modInverse(params.getQ());
+ BigInteger w = s.modInverse(q);
- BigInteger u1 = m.multiply(w).mod(params.getQ());
- BigInteger u2 = r.multiply(w).mod(params.getQ());
+ BigInteger u1 = m.multiply(w).mod(q);
+ BigInteger u2 = r.multiply(w).mod(q);
- u1 = params.getG().modPow(u1, params.getP());
- u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, params.getP());
+ BigInteger p = params.getP();
+ u1 = params.getG().modPow(u1, p);
+ u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, p);
- BigInteger v = u1.multiply(u2).mod(params.getP()).mod(params.getQ());
+ BigInteger v = u1.multiply(u2).mod(p).mod(q);
return v.equals(r);
}
@@ -157,4 +158,9 @@ public class DSASigner
return new BigInteger(1, trunc);
}
}
+
+ protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
+ {
+ return !needed ? null : (provided != null) ? provided : new SecureRandom();
+ }
}
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 0e769509..bceb8220 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
@@ -13,7 +13,9 @@ import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.util.Arrays;
/**
@@ -57,9 +59,9 @@ public class DSTU4145Signer
public BigInteger[] generateSignature(byte[] message)
{
- ECDomainParameters parameters = key.getParameters();
+ ECDomainParameters ec = key.getParameters();
- ECCurve curve = parameters.getCurve();
+ ECCurve curve = ec.getCurve();
ECFieldElement h = hash2FieldElement(curve, message);
if (h.isZero())
@@ -67,10 +69,14 @@ public class DSTU4145Signer
h = curve.fromBigInteger(ONE);
}
- BigInteger n = parameters.getN();
+ BigInteger n = ec.getN();
BigInteger e, r, s;
ECFieldElement Fe, y;
+ BigInteger d = ((ECPrivateKeyParameters)key).getD();
+
+ ECMultiplier basePointMultiplier = createBasePointMultiplier();
+
do
{
do
@@ -78,7 +84,7 @@ public class DSTU4145Signer
do
{
e = generateRandomInteger(n, random);
- Fe = parameters.getG().multiply(e).normalize().getAffineXCoord();
+ Fe = basePointMultiplier.multiply(ec.getG(), e).normalize().getAffineXCoord();
}
while (Fe.isZero());
@@ -87,7 +93,7 @@ public class DSTU4145Signer
}
while (r.signum() == 0);
- s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(n);
+ s = r.multiply(d).add(e).mod(n);
}
while (s.signum() == 0);
@@ -129,6 +135,11 @@ public class DSTU4145Signer
return fieldElement2Integer(n, y).compareTo(r) == 0;
}
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
+
/**
* Generates random integer such, than its bit length is less than that of n
*/
@@ -136,40 +147,24 @@ public class DSTU4145Signer
{
return new BigInteger(n.bitLength() - 1, random);
}
-
- private static void reverseBytes(byte[] bytes)
- {
- byte tmp;
-
- for (int i=0; i<bytes.length/2; i++)
- {
- tmp=bytes[i];
- bytes[i]=bytes[bytes.length-1-i];
- bytes[bytes.length-1-i]=tmp;
- }
- }
private static ECFieldElement hash2FieldElement(ECCurve curve, byte[] hash)
{
- byte[] data = Arrays.clone(hash);
- reverseBytes(data);
- BigInteger num = new BigInteger(1, data);
- while (num.bitLength() > curve.getFieldSize())
- {
- num = num.clearBit(num.bitLength() - 1);
- }
+ byte[] data = Arrays.reverse(hash);
+ return curve.fromBigInteger(truncate(new BigInteger(1, data), curve.getFieldSize()));
+ }
- return curve.fromBigInteger(num);
+ private static BigInteger fieldElement2Integer(BigInteger n, ECFieldElement fe)
+ {
+ return truncate(fe.toBigInteger(), n.bitLength() - 1);
}
- private static BigInteger fieldElement2Integer(BigInteger n, ECFieldElement fieldElement)
+ private static BigInteger truncate(BigInteger x, int bitLength)
{
- BigInteger num = fieldElement.toBigInteger();
- while (num.bitLength() >= n.bitLength())
+ if (x.bitLength() > bitLength)
{
- num = num.clearBit(num.bitLength() - 1);
+ x = x.mod(ONE.shiftLeft(bitLength));
}
-
- return num;
+ return x;
}
}
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 2a1f98eb..5fce1121 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
@@ -5,13 +5,16 @@ 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;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
/**
* EC-DSA as described in X9.62
@@ -46,18 +49,19 @@ public class ECDSASigner
boolean forSigning,
CipherParameters param)
{
+ SecureRandom providedRandom = null;
+
if (forSigning)
{
if (param instanceof ParametersWithRandom)
{
- ParametersWithRandom rParam = (ParametersWithRandom)param;
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
- this.random = rParam.getRandom();
this.key = (ECPrivateKeyParameters)rParam.getParameters();
+ providedRandom = rParam.getRandom();
}
else
{
- this.random = new SecureRandom();
this.key = (ECPrivateKeyParameters)param;
}
}
@@ -65,6 +69,8 @@ public class ECDSASigner
{
this.key = (ECPublicKeyParameters)param;
}
+
+ this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
}
// 5.3 pg 28
@@ -78,50 +84,44 @@ public class ECDSASigner
public BigInteger[] generateSignature(
byte[] message)
{
- BigInteger n = key.getParameters().getN();
+ ECDomainParameters ec = key.getParameters();
+ BigInteger n = ec.getN();
BigInteger e = calculateE(n, message);
- BigInteger r = null;
- BigInteger s = null;
+ BigInteger d = ((ECPrivateKeyParameters)key).getD();
if (kCalculator.isDeterministic())
{
- kCalculator.init(n, ((ECPrivateKeyParameters)key).getD(), message);
+ kCalculator.init(n, d, message);
}
else
{
kCalculator.init(n, random);
}
+ BigInteger r, s;
+
+ ECMultiplier basePointMultiplier = createBasePointMultiplier();
+
// 5.3.2
do // generate s
{
- BigInteger k = null;
-
+ BigInteger k;
do // generate r
{
k = kCalculator.nextK();
- ECPoint p = key.getParameters().getG().multiply(k).normalize();
+ ECPoint p = basePointMultiplier.multiply(ec.getG(), k).normalize();
// 5.3.3
- BigInteger x = p.getAffineXCoord().toBigInteger();
-
- r = x.mod(n);
+ r = p.getAffineXCoord().toBigInteger().mod(n);
}
while (r.equals(ZERO));
- BigInteger d = ((ECPrivateKeyParameters)key).getD();
-
s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
}
while (s.equals(ZERO));
- BigInteger[] res = new BigInteger[2];
-
- res[0] = r;
- res[1] = s;
-
- return res;
+ return new BigInteger[]{ r, s };
}
// 5.4 pg 29
@@ -135,7 +135,8 @@ public class ECDSASigner
BigInteger r,
BigInteger s)
{
- BigInteger n = key.getParameters().getN();
+ ECDomainParameters ec = key.getParameters();
+ BigInteger n = ec.getN();
BigInteger e = calculateE(n, message);
// r in the range [1,n-1]
@@ -155,7 +156,7 @@ public class ECDSASigner
BigInteger u1 = e.multiply(c).mod(n);
BigInteger u2 = r.multiply(c).mod(n);
- ECPoint G = key.getParameters().getG();
+ ECPoint G = ec.getG();
ECPoint Q = ((ECPublicKeyParameters)key).getQ();
ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2).normalize();
@@ -171,7 +172,7 @@ public class ECDSASigner
return v.equals(r);
}
- private BigInteger calculateE(BigInteger n, byte[] message)
+ protected BigInteger calculateE(BigInteger n, byte[] message)
{
int log2n = n.bitLength();
int messageBitLength = message.length * 8;
@@ -183,4 +184,14 @@ public class ECDSASigner
}
return e;
}
+
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
+
+ protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
+ {
+ return !needed ? null : (provided != null) ? provided : new SecureRandom();
+ }
}
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 f6d7f4fa..0ef88762 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
@@ -2,13 +2,16 @@ package org.bouncycastle.crypto.signers;
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;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import java.math.BigInteger;
import java.security.SecureRandom;
@@ -63,17 +66,20 @@ public class ECGOST3410Signer
{
mRev[i] = message[mRev.length - 1 - i];
}
-
+
BigInteger e = new BigInteger(1, mRev);
- BigInteger n = key.getParameters().getN();
- BigInteger r = null;
- BigInteger s = null;
+ ECDomainParameters ec = key.getParameters();
+ BigInteger n = ec.getN();
+ BigInteger d = ((ECPrivateKeyParameters)key).getD();
+
+ BigInteger r, s;
+
+ ECMultiplier basePointMultiplier = createBasePointMultiplier();
do // generate s
{
- BigInteger k = null;
-
+ BigInteger k;
do // generate r
{
do
@@ -82,26 +88,17 @@ public class ECGOST3410Signer
}
while (k.equals(ECConstants.ZERO));
- ECPoint p = key.getParameters().getG().multiply(k).normalize();
-
- BigInteger x = p.getAffineXCoord().toBigInteger();
+ ECPoint p = basePointMultiplier.multiply(ec.getG(), k).normalize();
- r = x.mod(n);
+ r = p.getAffineXCoord().toBigInteger().mod(n);
}
while (r.equals(ECConstants.ZERO));
- BigInteger d = ((ECPrivateKeyParameters)key).getD();
-
s = (k.multiply(e)).add(d.multiply(r)).mod(n);
}
while (s.equals(ECConstants.ZERO));
- BigInteger[] res = new BigInteger[2];
-
- res[0] = r;
- res[1] = s;
-
- return res;
+ return new BigInteger[]{ r, s };
}
/**
@@ -155,4 +152,9 @@ public class ECGOST3410Signer
return R.equals(r);
}
+
+ protected ECMultiplier createBasePointMultiplier()
+ {
+ return new FixedPointCombMultiplier();
+ }
}
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 72bbbcb4..3e839163 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
@@ -101,7 +101,7 @@ public class ECNRSigner
// BigInteger Vx = tempPair.getPublic().getW().getAffineX();
ECPublicKeyParameters V = (ECPublicKeyParameters)tempPair.getPublic(); // get temp's public key
- BigInteger Vx = V.getQ().normalize().getAffineXCoord().toBigInteger(); // get the point's x coordinate
+ BigInteger Vx = V.getQ().getAffineXCoord().toBigInteger(); // get the point's x coordinate
r = Vx.add(e).mod(n);
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java
index 9fcc41b9..135f1857 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java
@@ -1,11 +1,15 @@
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.*;
-
-import java.security.SecureRandom;
-import java.math.BigInteger;
+import org.bouncycastle.crypto.params.GOST3410KeyParameters;
+import org.bouncycastle.crypto.params.GOST3410Parameters;
+import org.bouncycastle.crypto.params.GOST3410PrivateKeyParameters;
+import org.bouncycastle.crypto.params.GOST3410PublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
/**
* GOST R 34.10-94 Signature Algorithm
@@ -25,7 +29,7 @@ public class GOST3410Signer
{
if (param instanceof ParametersWithRandom)
{
- ParametersWithRandom rParam = (ParametersWithRandom)param;
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
this.random = rParam.getRandom();
this.key = (GOST3410PrivateKeyParameters)rParam.getParameters();
@@ -99,7 +103,7 @@ public class GOST3410Signer
}
BigInteger m = new BigInteger(1, mRev);
- GOST3410Parameters params = key.getParameters();
+ GOST3410Parameters params = key.getParameters();
BigInteger zero = BigInteger.valueOf(0);
if (zero.compareTo(r) >= 0 || params.getQ().compareTo(r) <= 0)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java
index 6819e141..add087e7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/GenericSigner.java
@@ -121,6 +121,14 @@ public class GenericSigner
{
byte[] sig = engine.processBlock(signature, 0, signature.length);
+ // Extend with leading zeroes to match the digest size, if necessary.
+ if (sig.length < hash.length)
+ {
+ byte[] tmp = new byte[hash.length];
+ System.arraycopy(sig, 0, tmp, tmp.length - sig.length, sig.length);
+ sig = tmp;
+ }
+
return Arrays.constantTimeAreEqual(sig, hash);
}
catch (Exception 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
index b96e3f37..0fb7375f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
@@ -61,7 +61,7 @@ public class HMacDSAKCalculator
BigInteger mInt = bitsToInt(message);
- if (mInt.compareTo(n) > 0)
+ if (mInt.compareTo(n) >= 0)
{
mInt = mInt.subtract(n);
}
@@ -113,37 +113,28 @@ public class HMacDSAKCalculator
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;
- }
+ int len = Math.min(t.length - tOff, V.length);
+ System.arraycopy(V, 0, t, tOff, len);
+ tOff += len;
}
BigInteger k = bitsToInt(t);
- if (k.equals(ZERO) || k.compareTo(n) >= 0)
+ if (k.compareTo(ZERO) > 0 && k.compareTo(n) < 0)
{
- hMac.update(V, 0, V.length);
- hMac.update((byte)0x00);
+ return k;
+ }
- hMac.doFinal(K, 0);
+ hMac.update(V, 0, V.length);
+ hMac.update((byte)0x00);
- hMac.init(new KeyParameter(K));
+ hMac.doFinal(K, 0);
- hMac.update(V, 0, V.length);
+ hMac.init(new KeyParameter(K));
- hMac.doFinal(V, 0);
- }
- else
- {
- return k;
- }
+ hMac.update(V, 0, V.length);
+
+ hMac.doFinal(V, 0);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
index e3dcc08e..fcd5816a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java
@@ -17,7 +17,7 @@ import org.bouncycastle.util.Integers;
/**
* ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).
- * <p/>
+ * <p>
* Note: the usual length for the salt is the length of the hash
* function used in bytes.
*/
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 aaae0645..69d9918f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
@@ -48,6 +48,8 @@ public class RSADigestSigner
oidMap.put("SHA-256", NISTObjectIdentifiers.id_sha256);
oidMap.put("SHA-384", NISTObjectIdentifiers.id_sha384);
oidMap.put("SHA-512", NISTObjectIdentifiers.id_sha512);
+ oidMap.put("SHA-512/224", NISTObjectIdentifiers.id_sha512_224);
+ oidMap.put("SHA-512/256", NISTObjectIdentifiers.id_sha512_256);
oidMap.put("MD2", PKCSObjectIdentifiers.md2);
oidMap.put("MD4", PKCSObjectIdentifiers.md4);
@@ -218,6 +220,8 @@ public class RSADigestSigner
}
else
{
+ Arrays.constantTimeAreEqual(expected, expected); // keep time "steady".
+
return false;
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
index bbd8cda6..6a693080 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
@@ -3,7 +3,7 @@ package org.bouncycastle.crypto.signers;
import java.math.BigInteger;
import java.security.SecureRandom;
-class RandomDSAKCalculator
+public class RandomDSAKCalculator
implements DSAKCalculator
{
private static final BigInteger ZERO = BigInteger.valueOf(0);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/X931Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/X931Signer.java
new file mode 100644
index 00000000..71974044
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/X931Signer.java
@@ -0,0 +1,269 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.util.Hashtable;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.SignerWithRecovery;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * X9.31-1998 - signing using a hash.
+ * <p>
+ * The message digest hash, H, is encapsulated to form a byte string as follows
+ * <pre>
+ * EB = 06 || PS || 0xBA || H || TRAILER
+ * </pre>
+ * where PS is a string of bytes all of value 0xBB of length such that |EB|=|n|, and TRAILER is the ISO/IEC 10118 part number† for the digest. The byte string, EB, is converted to an integer value, the message representative, f.
+ */
+public class X931Signer
+ implements Signer
+{
+ static final public int TRAILER_IMPLICIT = 0xBC;
+ static final public int TRAILER_RIPEMD160 = 0x31CC;
+ static final public int TRAILER_RIPEMD128 = 0x32CC;
+ static final public int TRAILER_SHA1 = 0x33CC;
+ static final public int TRAILER_SHA256 = 0x34CC;
+ static final public int TRAILER_SHA512 = 0x35CC;
+ static final public int TRAILER_SHA384 = 0x36CC;
+ static final public int TRAILER_WHIRLPOOL = 0x37CC;
+ static final public int TRAILER_SHA224 = 0x38CC;
+
+ private static Hashtable trailerMap = new Hashtable();
+
+ static
+ {
+ trailerMap.put("RIPEMD128", Integers.valueOf(TRAILER_RIPEMD128));
+ trailerMap.put("RIPEMD160", Integers.valueOf(TRAILER_RIPEMD160));
+
+ trailerMap.put("SHA-1", Integers.valueOf(TRAILER_SHA1));
+ trailerMap.put("SHA-224", Integers.valueOf(TRAILER_SHA224));
+ trailerMap.put("SHA-256", Integers.valueOf(TRAILER_SHA256));
+ trailerMap.put("SHA-384", Integers.valueOf(TRAILER_SHA384));
+ trailerMap.put("SHA-512", Integers.valueOf(TRAILER_SHA512));
+
+ trailerMap.put("Whirlpool", Integers.valueOf(TRAILER_WHIRLPOOL));
+ }
+
+ private Digest digest;
+ private AsymmetricBlockCipher cipher;
+ private RSAKeyParameters kParam;
+
+ private int trailer;
+ private int keyBits;
+ private byte[] block;
+
+ /**
+ * Generate a signer for the with either implicit or explicit trailers
+ * for ISO9796-2.
+ *
+ * @param cipher base cipher to use for signature creation/verification
+ * @param digest digest to use.
+ * @param implicit whether or not the trailer is implicit or gives the hash.
+ */
+ public X931Signer(
+ AsymmetricBlockCipher cipher,
+ Digest digest,
+ boolean implicit)
+ {
+ this.cipher = cipher;
+ this.digest = digest;
+
+ if (implicit)
+ {
+ trailer = TRAILER_IMPLICIT;
+ }
+ else
+ {
+ Integer trailerObj = (Integer)trailerMap.get(digest.getAlgorithmName());
+
+ if (trailerObj != null)
+ {
+ trailer = trailerObj.intValue();
+ }
+ else
+ {
+ throw new IllegalArgumentException("no valid trailer for digest");
+ }
+ }
+ }
+
+ /**
+ * Constructor for a signer with an explicit digest trailer.
+ *
+ * @param cipher cipher to use.
+ * @param digest digest to sign with.
+ */
+ public X931Signer(
+ AsymmetricBlockCipher cipher,
+ Digest digest)
+ {
+ this(cipher, digest, false);
+ }
+
+ public void init(
+ boolean forSigning,
+ CipherParameters param)
+ {
+ kParam = (RSAKeyParameters)param;
+
+ cipher.init(forSigning, kParam);
+
+ keyBits = kParam.getModulus().bitLength();
+
+ block = new byte[(keyBits + 7) / 8];
+
+ reset();
+ }
+
+ /**
+ * clear possible sensitive data
+ */
+ private void clearBlock(
+ byte[] block)
+ {
+ for (int i = 0; i != block.length; i++)
+ {
+ block[i] = 0;
+ }
+ }
+
+ /**
+ * update the internal digest with the byte b
+ */
+ public void update(
+ byte b)
+ {
+ digest.update(b);
+ }
+
+ /**
+ * update the internal digest with the byte array in
+ */
+ public void update(
+ byte[] in,
+ int off,
+ int len)
+ {
+ digest.update(in, off, len);
+ }
+
+ /**
+ * reset the internal state
+ */
+ public void reset()
+ {
+ digest.reset();
+ }
+
+ /**
+ * generate a signature for the loaded message using the key we were
+ * initialised with.
+ */
+ public byte[] generateSignature()
+ throws CryptoException
+ {
+ createSignatureBlock();
+
+ BigInteger t = new BigInteger(1, cipher.processBlock(block, 0, block.length));
+ BigInteger nSubT = kParam.getModulus().subtract(t);
+
+ clearBlock(block);
+
+ BigInteger v = kParam.getModulus().shiftRight(2);
+
+ if (t.compareTo(nSubT) > 0)
+ {
+ return BigIntegers.asUnsignedByteArray((kParam.getModulus().bitLength() + 7) / 8, nSubT);
+ }
+ else
+ {
+ return BigIntegers.asUnsignedByteArray((kParam.getModulus().bitLength() + 7) / 8, t);
+ }
+ }
+
+ private void createSignatureBlock()
+ {
+ int digSize = digest.getDigestSize();
+
+ int delta;
+
+ if (trailer == TRAILER_IMPLICIT)
+ {
+ delta = block.length - digSize - 1;
+ digest.doFinal(block, delta);
+ block[block.length - 1] = (byte)TRAILER_IMPLICIT;
+ }
+ else
+ {
+ delta = block.length - digSize - 2;
+ digest.doFinal(block, delta);
+ block[block.length - 2] = (byte)(trailer >>> 8);
+ block[block.length - 1] = (byte)trailer;
+ }
+
+ block[0] = 0x6b;
+ for (int i = delta - 2; i != 0; i--)
+ {
+ block[i] = (byte)0xbb;
+ }
+ block[delta - 1] = (byte)0xba;
+ }
+
+ /**
+ * return true if the signature represents a ISO9796-2 signature
+ * for the passed in message.
+ */
+ public boolean verifySignature(
+ byte[] signature)
+ {
+ try
+ {
+ block = cipher.processBlock(signature, 0, signature.length);
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+
+ BigInteger t = new BigInteger(block);
+ BigInteger f;
+
+ if (t.mod(BigInteger.valueOf(16)).equals(BigInteger.valueOf(12)))
+ {
+ f = t;
+ }
+ else
+ {
+ t = kParam.getModulus().subtract(t);
+ if (t.mod(BigInteger.valueOf(16)).equals(BigInteger.valueOf(12)))
+ {
+ f = t;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ createSignatureBlock();
+
+ byte[] fBlock = BigIntegers.asUnsignedByteArray(block.length, f);
+
+ boolean rv = Arrays.constantTimeAreEqual(block, fBlock);
+
+ clearBlock(block);
+ clearBlock(fBlock);
+
+ return rv;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html
new file mode 100644
index 00000000..151d3d5e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Basic signers.
+</body>
+</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/AEADTestUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/AEADTestUtil.java
new file mode 100644
index 00000000..5ffa72a1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/AEADTestUtil.java
@@ -0,0 +1,474 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestFailedException;
+
+public class AEADTestUtil
+{
+ public static void testTampering(Test test, AEADBlockCipher cipher, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ byte[] plaintext = new byte[1000];
+ for (int i = 0; i < plaintext.length; i++)
+ {
+ plaintext[i] = (byte)i;
+ }
+ cipher.init(true, params);
+
+ byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
+ int len = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
+ cipher.doFinal(ciphertext, len);
+
+ int macLength = cipher.getMac().length;
+
+ // Test tampering with a single byte
+ cipher.init(false, params);
+ byte[] tampered = new byte[ciphertext.length];
+ byte[] output = new byte[plaintext.length];
+ System.arraycopy(ciphertext, 0, tampered, 0, tampered.length);
+ tampered[0] += 1;
+
+ cipher.processBytes(tampered, 0, tampered.length, output, 0);
+ try
+ {
+ cipher.doFinal(output, 0);
+ throw new TestFailedException(
+ new SimpleTestResult(false, test + " : tampering of ciphertext not detected."));
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // Expected
+ }
+
+ // Test truncation of ciphertext to < tag length
+ cipher.init(false, params);
+ byte[] truncated = new byte[macLength - 1];
+ System.arraycopy(ciphertext, 0, truncated, 0, truncated.length);
+
+ cipher.processBytes(truncated, 0, truncated.length, output, 0);
+ try
+ {
+ cipher.doFinal(output, 0);
+ fail(test, "tampering of ciphertext not detected.");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // Expected
+ }
+ }
+
+ private static void fail(Test test, String message)
+ {
+ throw new TestFailedException(SimpleTestResult.failed(test, message));
+ }
+
+ private static void fail(Test test, String message, String expected, String result)
+ {
+ throw new TestFailedException(SimpleTestResult.failed(test, message, expected, result));
+ }
+
+ public static void testReset(Test test, AEADBlockCipher cipher1, AEADBlockCipher cipher2, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ cipher1.init(true, params);
+
+ byte[] plaintext = new byte[1000];
+ byte[] ciphertext = new byte[cipher1.getOutputSize(plaintext.length)];
+
+ // Establish baseline answer
+ crypt(cipher1, plaintext, ciphertext);
+
+ // Test encryption resets
+ checkReset(test, cipher1, params, true, plaintext, ciphertext);
+
+ // Test decryption resets with fresh instance
+ cipher2.init(false, params);
+ checkReset(test, cipher2, params, false, ciphertext, plaintext);
+ }
+
+ private static void checkReset(Test test,
+ AEADBlockCipher cipher,
+ CipherParameters params,
+ boolean encrypt,
+ byte[] pretext,
+ byte[] posttext)
+ throws InvalidCipherTextException
+ {
+ // Do initial run
+ byte[] output = new byte[posttext.length];
+ crypt(cipher, pretext, output);
+
+ // Check encrypt resets cipher
+ crypt(cipher, pretext, output);
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, (encrypt ? "Encrypt" : "Decrypt") + " did not reset cipher.");
+ }
+
+ // Check init resets data
+ cipher.processBytes(pretext, 0, 100, output, 0);
+ cipher.init(encrypt, params);
+
+ try
+ {
+ crypt(cipher, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test, "Init did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, "Init did not reset data.", new String(Hex.encode(posttext)), new String(Hex.encode(output)));
+ }
+
+ // Check init resets AD
+ cipher.processAADBytes(pretext, 0, 100);
+ cipher.init(encrypt, params);
+
+ try
+ {
+ crypt(cipher, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test, "Init did not reset additional data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, "Init did not reset additional data.");
+ }
+
+ // Check reset resets data
+ cipher.processBytes(pretext, 0, 100, output, 0);
+ cipher.reset();
+
+ try
+ {
+ crypt(cipher, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test, "Init did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, "Reset did not reset data.");
+ }
+
+ // Check reset resets AD
+ cipher.processAADBytes(pretext, 0, 100);
+ cipher.reset();
+
+ try
+ {
+ crypt(cipher, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test, "Init did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test, "Reset did not reset additional data.");
+ }
+ }
+
+ private static void crypt(AEADBlockCipher cipher, byte[] plaintext, byte[] output)
+ throws InvalidCipherTextException
+ {
+ int len = cipher.processBytes(plaintext, 0, plaintext.length, output, 0);
+ cipher.doFinal(output, len);
+ }
+
+ public static void testOutputSizes(Test test, AEADBlockCipher cipher, AEADParameters params)
+ throws IllegalStateException,
+ InvalidCipherTextException
+ {
+ int maxPlaintext = cipher.getUnderlyingCipher().getBlockSize() * 10;
+ byte[] plaintext = new byte[maxPlaintext];
+ byte[] ciphertext = new byte[maxPlaintext * 2];
+
+ // Check output size calculations for truncated ciphertext lengths
+ cipher.init(true, params);
+ cipher.doFinal(ciphertext, 0);
+ int macLength = cipher.getMac().length;
+
+ cipher.init(false, params);
+ for (int i = 0; i < macLength; i++)
+ {
+ cipher.reset();
+ if (cipher.getUpdateOutputSize(i) != 0)
+ {
+ fail(test, "AE cipher should not produce update output with ciphertext length <= macSize");
+ }
+ if (cipher.getOutputSize(i) != 0)
+ {
+ fail(test, "AE cipher should not produce output with ciphertext length <= macSize");
+ }
+ }
+
+ for (int i = 0; i < plaintext.length; i++)
+ {
+ cipher.init(true, params);
+ int expectedCTUpdateSize = cipher.getUpdateOutputSize(i);
+ int expectedCTOutputSize = cipher.getOutputSize(i);
+
+ if (expectedCTUpdateSize < 0)
+ {
+ fail(test, "Encryption update output size should not be < 0 for size " + i);
+ }
+
+ if (expectedCTOutputSize < 0)
+ {
+ fail(test, "Encryption update output size should not be < 0 for size " + i);
+ }
+
+ int actualCTSize = cipher.processBytes(plaintext, 0, i, ciphertext, 0);
+
+ if (expectedCTUpdateSize != actualCTSize)
+ {
+ fail(test, "Encryption update output size did not match calculated for plaintext length " + i,
+ String.valueOf(expectedCTUpdateSize), String.valueOf(actualCTSize));
+ }
+
+ actualCTSize += cipher.doFinal(ciphertext, actualCTSize);
+
+ if (expectedCTOutputSize != actualCTSize)
+ {
+ fail(test, "Encryption actual final output size did not match calculated for plaintext length " + i,
+ String.valueOf(expectedCTOutputSize), String.valueOf(actualCTSize));
+ }
+
+ cipher.init(false, params);
+ int expectedPTUpdateSize = cipher.getUpdateOutputSize(actualCTSize);
+ int expectedPTOutputSize = cipher.getOutputSize(actualCTSize);
+
+ if (expectedPTOutputSize != i)
+ {
+ fail(test, "Decryption update output size did not original plaintext length " + i,
+ String.valueOf(expectedPTUpdateSize), String.valueOf(i));
+ }
+
+ int actualPTSize = cipher.processBytes(ciphertext, 0, actualCTSize, plaintext, 0);
+
+ if (expectedPTUpdateSize != actualPTSize)
+ {
+ fail(test, "Decryption update output size did not match calculated for plaintext length " + i,
+ String.valueOf(expectedPTUpdateSize), String.valueOf(actualPTSize));
+ }
+
+ actualPTSize += cipher.doFinal(plaintext, actualPTSize);
+
+ if (expectedPTOutputSize != actualPTSize)
+ {
+ fail(test, "Decryption update output size did not match calculated for plaintext length " + i,
+ String.valueOf(expectedPTOutputSize), String.valueOf(actualPTSize));
+ }
+
+ }
+ }
+
+ public static void testBufferSizeChecks(Test test, AEADBlockCipher cipher, AEADParameters params)
+ throws IllegalStateException,
+ InvalidCipherTextException
+ {
+ int blockSize = cipher.getUnderlyingCipher().getBlockSize();
+ int maxPlaintext = (blockSize * 10);
+ byte[] plaintext = new byte[maxPlaintext];
+
+
+ cipher.init(true, params);
+
+ int expectedUpdateOutputSize = cipher.getUpdateOutputSize(plaintext.length);
+ byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
+
+ try
+ {
+ cipher.processBytes(new byte[maxPlaintext - 1], 0, maxPlaintext, new byte[expectedUpdateOutputSize], 0);
+ fail(test, "processBytes should validate input buffer length");
+ }
+ catch (DataLengthException e)
+ {
+ // Expected
+ }
+ cipher.reset();
+
+ if (expectedUpdateOutputSize > 0)
+ {
+ int outputTrigger = 0;
+ // Process bytes until output would be produced
+ for(int i = 0; i < plaintext.length; i++) {
+ if (cipher.getUpdateOutputSize(1) != 0)
+ {
+ outputTrigger = i + 1;
+ break;
+ }
+ cipher.processByte(plaintext[i], ciphertext, 0);
+ }
+ if (outputTrigger == 0)
+ {
+ fail(test, "Failed to find output trigger size");
+ }
+ try
+ {
+ cipher.processByte(plaintext[0], new byte[cipher.getUpdateOutputSize(1) - 1], 0);
+ fail(test, "Encrypt processByte should validate output buffer length");
+ }
+ catch (OutputLengthException e)
+ {
+ // Expected
+ }
+ cipher.reset();
+
+ // Repeat checking with entire input at once
+ try
+ {
+ cipher.processBytes(plaintext, 0, outputTrigger,
+ new byte[cipher.getUpdateOutputSize(outputTrigger) - 1], 0);
+ fail(test, "Encrypt processBytes should validate output buffer length");
+ }
+ catch (OutputLengthException e)
+ {
+ // Expected
+ }
+ cipher.reset();
+
+ }
+
+ // Remember the actual ciphertext for later
+ int actualOutputSize = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
+ actualOutputSize += cipher.doFinal(ciphertext, actualOutputSize);
+ int macSize = cipher.getMac().length;
+
+ cipher.reset();
+ try
+ {
+ cipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
+ cipher.doFinal(new byte[cipher.getOutputSize(0) - 1], 0);
+ fail(test, "Encrypt doFinal should validate output buffer length");
+ }
+ catch (OutputLengthException e)
+ {
+ // Expected
+ }
+
+ // Decryption tests
+
+ cipher.init(false, params);
+ expectedUpdateOutputSize = cipher.getUpdateOutputSize(actualOutputSize);
+
+ if (expectedUpdateOutputSize > 0)
+ {
+ // Process bytes until output would be produced
+ int outputTrigger = 0;
+ for (int i = 0; i < plaintext.length; i++)
+ {
+ if (cipher.getUpdateOutputSize(1) != 0)
+ {
+ outputTrigger = i + 1;
+ break;
+ }
+ cipher.processByte(ciphertext[i], plaintext, 0);
+ }
+ if (outputTrigger == 0)
+ {
+ fail(test, "Failed to find output trigger size");
+ }
+
+ try
+ {
+ cipher.processByte(ciphertext[0], new byte[cipher.getUpdateOutputSize(1) - 1], 0);
+ fail(test, "Decrypt processByte should validate output buffer length");
+ }
+ catch (OutputLengthException e)
+ {
+ // Expected
+ }
+ cipher.reset();
+
+ // Repeat test with processBytes
+ try
+ {
+ cipher.processBytes(ciphertext, 0, outputTrigger,
+ new byte[cipher.getUpdateOutputSize(outputTrigger) - 1], 0);
+ fail(test, "Decrypt processBytes should validate output buffer length");
+ }
+ catch (OutputLengthException e)
+ {
+ // Expected
+ }
+ }
+
+ cipher.reset();
+ // Data less than mac length should fail before output length check
+ try
+ {
+ // Assumes AE cipher on decrypt can't return any data until macSize bytes are received
+ if (cipher.processBytes(ciphertext, 0, macSize - 1, plaintext, 0) != 0)
+ {
+ fail(test, "AE cipher unexpectedly produced output");
+ }
+ cipher.doFinal(new byte[0], 0);
+ fail(test, "Decrypt doFinal should check ciphertext length");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ // Search through plaintext lengths until one is found that creates >= 1 buffered byte
+ // during decryption of ciphertext for doFinal to handle
+ for (int i = 2; i < plaintext.length; i++)
+ {
+ cipher.init(true, params);
+ int encrypted = cipher.processBytes(plaintext, 0, i, ciphertext, 0);
+ encrypted += cipher.doFinal(ciphertext, encrypted);
+
+ cipher.init(false, params);
+ cipher.processBytes(ciphertext, 0, encrypted - 1, plaintext, 0);
+ if (cipher.processByte(ciphertext[encrypted - 1], plaintext, 0) == 0)
+ {
+ cipher.doFinal(new byte[cipher.getOutputSize(0) - 1], 0);
+ fail(test, "Decrypt doFinal should check output length");
+ cipher.reset();
+
+ // Truncated Mac should be reported in preference to inability to output
+ // buffered plaintext byte
+ try
+ {
+ cipher.processBytes(ciphertext, 0, actualOutputSize - 1, plaintext, 0);
+ cipher.doFinal(new byte[cipher.getOutputSize(0) - 1], 0);
+ fail(test, "Decrypt doFinal should check ciphertext length");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // Expected
+ }
+ cipher.reset();
+ }
+ }
+ fail(test, "Decrypt doFinal test couldn't find a ciphertext length that buffered for doFinal");
+ }
+ catch (OutputLengthException e)
+ {
+ // Expected
+ }
+ }
+
+ static AEADParameters reuseKey(AEADParameters p)
+ {
+ return new AEADParameters(null, p.getMacSize(), p.getNonce(), p.getAssociatedText());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/AESFastTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESFastTest.java
new file mode 100644
index 00000000..c94b1182
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESFastTest.java
@@ -0,0 +1,150 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ */
+public class AESFastTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new AESFastEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(1, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(2, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(3, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(4, new AESFastEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(5, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(6, new AESFastEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(7, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(8, new AESFastEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(9, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(10, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(11, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(12, new AESFastEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(13, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(14, new AESFastEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(15, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(16, new AESFastEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(17, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(18, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(19, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(20, new AESFastEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(21, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(22, new AESFastEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(23, 10000, new AESFastEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168")
+ };
+
+ private BlockCipher _engine = new AESFastEngine();
+
+ AESFastTest()
+ {
+ super(tests, new AESFastEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "AESFast";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ byte[] keyBytes = new byte[16];
+
+ _engine.init(true, new KeyParameter(keyBytes));
+
+ //
+ // init tests
+ //
+ try
+ {
+ byte[] dudKey = new byte[6];
+
+ _engine.init(true, new KeyParameter(dudKey));
+
+ fail("failed key length check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ byte[] iv = new byte[16];
+
+ _engine.init(true, new ParametersWithIV(null, iv));
+
+ fail("failed parameter check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new AESFastTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/AESLightTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESLightTest.java
new file mode 100644
index 00000000..5e989f42
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESLightTest.java
@@ -0,0 +1,150 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.AESLightEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ */
+public class AESLightTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new AESLightEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(1, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(2, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(3, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(4, new AESLightEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(5, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(6, new AESLightEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(7, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(8, new AESLightEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(9, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(10, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(11, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(12, new AESLightEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(13, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(14, new AESLightEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(15, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(16, new AESLightEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(17, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(18, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(19, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(20, new AESLightEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(21, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(22, new AESLightEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(23, 10000, new AESLightEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168")
+ };
+
+ private BlockCipher _engine = new AESLightEngine();
+
+ AESLightTest()
+ {
+ super(tests, new AESLightEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "AESLight";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ byte[] keyBytes = new byte[16];
+
+ _engine.init(true, new KeyParameter(keyBytes));
+
+ //
+ // init tests
+ //
+ try
+ {
+ byte[] dudKey = new byte[6];
+
+ _engine.init(true, new KeyParameter(dudKey));
+
+ fail("failed key length check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ byte[] iv = new byte[16];
+
+ _engine.init(true, new ParametersWithIV(null, iv));
+
+ fail("failed parameter check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new AESLightTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/AESTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESTest.java
new file mode 100644
index 00000000..5585d8c1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESTest.java
@@ -0,0 +1,442 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.crypto.modes.SICBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ */
+public class AESTest
+ extends CipherTest
+{
+ private static final byte[] tData = Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114F3F6752AE8D7831138F041560631B1145A01020304050607");
+ private static final byte[] outCBC1 = Hex.decode("a444a9a4d46eb30cb7ed34d62873a89f8fdf2bf8a54e1aeadd06fd85c9cb46f021ee7cd4f418fa0bb72e9d07c70d5d20");
+ private static final byte[] outCBC2 = Hex.decode("585681354f0e01a86b32f94ebb6a675045d923cf201263c2aaecca2b4de82da0edd74ca5efd654c688f8a58e61955b11");
+ private static final byte[] outSIC1 = Hex.decode("82a1744e8ebbd053ca72362d5e570326e0b6fdaf824ab673fbf029042886b23c75129a015852913790f81f94447475a0");
+ private static final byte[] outSIC2 = Hex.decode("146cbb581d9e12c3333dd9c736fbb93043c92019f78580da48f81f80b3f551d58ea836fed480fc6912fefa9c5c89cc24");
+ private static final byte[] outCFB1 = Hex.decode("82a1744e8ebbd053ca72362d5e5703264b4182de3208c374b8ac4fa36af9c5e5f4f87d1e3b67963d06acf5eb13914c90");
+ private static final byte[] outCFB2 = Hex.decode("146cbb581d9e12c3333dd9c736fbb9303c8a3eb5185e2809e9d3c28e25cc2d2b6f5c11ee28d6530f72c412b1438a816a");
+ private static final byte[] outOFB1 = Hex.decode("82a1744e8ebbd053ca72362d5e5703261ebf1fdbec05e57b3465b583132f84b43bf95b2c89040ad1677b22d42db69a7a");
+ private static final byte[] outOFB2 = Hex.decode("146cbb581d9e12c3333dd9c736fbb9309ea4c2a7696c84959a2dada49f2f1c5905db1f0cec3a31acbc4701e74ab05e1f");
+
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new AESEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(1, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(2, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(3, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(4, new AESEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(5, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(6, new AESEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(7, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(8, new AESEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(9, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(10, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(11, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(12, new AESEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(13, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(14, new AESEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(15, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(16, new AESEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(17, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(18, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(19, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(20, new AESEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(21, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(22, new AESEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(23, 10000, new AESEngine(),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168")
+ };
+
+ private BlockCipher _engine = new AESEngine();
+
+ public AESTest()
+ {
+ super(tests, new AESEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "AES";
+ }
+
+ private void testNullSIC()
+ throws InvalidCipherTextException
+ {
+ BufferedBlockCipher b = new BufferedBlockCipher(new SICBlockCipher(new AESEngine()));
+ KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
+
+ b.init(true, new ParametersWithIV(kp, new byte[16]));
+
+ byte[] out = new byte[b.getOutputSize(tData.length)];
+
+ int len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outSIC1, out))
+ {
+ fail("no match on first nullSIC check");
+ }
+
+ b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outSIC2, out))
+ {
+ fail("no match on second nullSIC check");
+ }
+ }
+
+ private void testNullCBC()
+ throws InvalidCipherTextException
+ {
+ BufferedBlockCipher b = new BufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
+ KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
+
+ b.init(true, new ParametersWithIV(kp, new byte[16]));
+
+ byte[] out = new byte[b.getOutputSize(tData.length)];
+
+ int len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outCBC1, out))
+ {
+ fail("no match on first nullCBC check");
+ }
+
+ b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outCBC2, out))
+ {
+ fail("no match on second nullCBC check");
+ }
+ }
+
+ private void testNullOFB()
+ throws InvalidCipherTextException
+ {
+ BufferedBlockCipher b = new BufferedBlockCipher(new OFBBlockCipher(new AESEngine(), 128));
+ KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
+
+ b.init(true, new ParametersWithIV(kp, new byte[16]));
+
+ byte[] out = new byte[b.getOutputSize(tData.length)];
+
+ int len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outOFB1, out))
+ {
+ fail("no match on first nullOFB check");
+ }
+
+ b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outOFB2, out))
+ {
+ fail("no match on second nullOFB check");
+ }
+ }
+
+ private void testNullCFB()
+ throws InvalidCipherTextException
+ {
+ BufferedBlockCipher b = new BufferedBlockCipher(new CFBBlockCipher(new AESEngine(), 128));
+ KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
+
+ b.init(true, new ParametersWithIV(kp, new byte[16]));
+
+ byte[] out = new byte[b.getOutputSize(tData.length)];
+
+ int len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outCFB1, out))
+ {
+ fail("no match on first nullCFB check");
+ }
+
+ b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ len = b.processBytes(tData, 0, tData.length, out, 0);
+
+ len += b.doFinal(out, len);
+
+ if (!areEqual(outCFB2, out))
+ {
+ fail("no match on second nullCFB check");
+ }
+ }
+
+ private boolean areEqual(byte[] a, int aOff, byte[] b, int bOff)
+ {
+ for (int i = bOff; i != b.length; i++)
+ {
+ if (a[aOff + i - bOff] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void skipTest()
+ {
+ CipherParameters params = new ParametersWithIV(new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")), Hex.decode("00000000000000000000000000000000"));
+ SICBlockCipher engine = new SICBlockCipher(new AESEngine());
+
+ engine.init(true, params);
+
+ SecureRandom rand = new SecureRandom();
+ byte[] plain = new byte[50000];
+ byte[] cipher = new byte[50000];
+
+ rand.nextBytes(plain);
+ engine.processBytes(plain, 0, plain.length, cipher, 0);
+
+ byte[] fragment = new byte[20];
+
+ engine.init(true, params);
+
+ engine.skip(10);
+
+ if (engine.getPosition() != 10)
+ {
+ fail("skip position incorrect - 10 got " + engine.getPosition());
+ }
+
+ engine.processBytes(plain, 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 10, fragment, 0))
+ {
+ fail("skip forward 10 failed");
+ }
+
+ engine.skip(1000);
+
+ if (engine.getPosition() != 1010 + fragment.length)
+ {
+ fail("skip position incorrect - " + (1010 + fragment.length) + " got " + engine.getPosition());
+ }
+
+ engine.processBytes(plain, 1010 + fragment.length, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + fragment.length, fragment, 0))
+ {
+ fail("skip forward 1000 failed");
+ }
+
+ engine.skip(-10);
+
+ if (engine.getPosition() != 1010 + 2 * fragment.length - 10)
+ {
+ fail("skip position incorrect - " + (1010 + 2 * fragment.length - 10) + " got " + engine.getPosition());
+ }
+
+ engine.processBytes(plain, 1010 + 2 * fragment.length - 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + 2 * fragment.length - 10, fragment, 0))
+ {
+ fail("skip back 10 failed");
+ }
+
+ engine.skip(-1000);
+
+ if (engine.getPosition() != 60)
+ {
+ fail("skip position incorrect - " + 60 + " got " + engine.getPosition());
+ }
+
+ engine.processBytes(plain, 60, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 60, fragment, 0))
+ {
+ fail("skip back 1000 failed");
+ }
+
+ long pos = engine.seekTo(1010);
+
+ if (pos != 1010)
+ {
+ fail("position incorrect - " + 1010 + " got " + pos);
+ }
+
+ engine.processBytes(plain, 1010, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010, fragment, 0))
+ {
+ fail("seek to 1010 failed");
+ }
+
+ engine.reset();
+
+ for (int i = 0; i != 5000; i++)
+ {
+ engine.skip(i);
+
+ if (engine.getPosition() != i)
+ {
+ fail("skip forward at wrong position");
+ }
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip forward i failed: " + i);
+ }
+
+ if (engine.getPosition() != i + fragment.length)
+ {
+ fail("cipher at wrong position: " + engine.getPosition() + " [" + i + "]");
+ }
+
+ engine.skip(-fragment.length);
+
+ if (engine.getPosition() != i)
+ {
+ fail("skip back at wrong position");
+ }
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip back i failed: " + i);
+ }
+
+ engine.reset();
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ byte[] keyBytes = new byte[16];
+
+ _engine.init(true, new KeyParameter(keyBytes));
+
+ //
+ // init tests
+ //
+ try
+ {
+ byte[] dudKey = new byte[6];
+
+ _engine.init(true, new KeyParameter(dudKey));
+
+ fail("failed key length check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ byte[] iv = new byte[16];
+
+ _engine.init(true, new ParametersWithIV(null, iv));
+
+ fail("failed parameter check");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ testNullCBC();
+ testNullSIC();
+ testNullOFB();
+ testNullCFB();
+
+ skipTest();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new AESTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/AESVectorFileTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESVectorFileTest.java
new file mode 100644
index 00000000..cfd48430
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESVectorFileTest.java
@@ -0,0 +1,258 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.AESLightEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ */
+public class AESVectorFileTest
+ implements Test
+{
+
+ private int countOfTests = 0;
+ private int testNum = 0;
+
+ protected BlockCipher createNewEngineForTest()
+ {
+ return new AESEngine();
+ }
+
+ private Test[] readTestVectors(InputStream inStream)
+ {
+ // initialize key, plaintext, ciphertext = null
+ // read until find BLOCKSIZE=
+ // return if not 128
+ // read KEYSIZE= or ignore
+ // loop
+ // read a line
+ // if starts with BLOCKSIZE=
+ // parse the rest. return if not 128
+ // if starts with KEY=
+ // parse the rest and set KEY
+ // if starts with PT=
+ // parse the rest and set plaintext
+ // if starts with CT=
+ // parse the rest and set ciphertext
+ // if starts with TEST= or end of file
+ // if key, plaintext, ciphertext are all not null
+ // save away their values as the next test
+ // until end of file
+ List tests = new ArrayList();
+ String key = null;
+ String plaintext = null;
+ String ciphertext = null;
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(inStream));
+
+ try
+ {
+ String line = in.readLine();
+
+ while (line != null)
+ {
+ line = line.trim().toLowerCase();
+ if (line.startsWith("blocksize="))
+ {
+ int i = 0;
+ try
+ {
+ i = Integer.parseInt(line.substring(10).trim());
+ }
+ catch (Exception e)
+ {
+ }
+ if (i != 128)
+ {
+ return null;
+ }
+ }
+ else if (line.startsWith("keysize="))
+ {
+ int i = 0;
+ try
+ {
+ i = Integer.parseInt(line.substring(10).trim());
+ }
+ catch (Exception e)
+ {
+ }
+ if ((i != 128) && (i != 192) && (i != 256))
+ {
+ return null;
+ }
+ }
+ else if (line.startsWith("key="))
+ {
+ key = line.substring(4).trim();
+ }
+ else if (line.startsWith("pt="))
+ {
+ plaintext = line.substring(3).trim();
+ }
+ else if (line.startsWith("ct="))
+ {
+ ciphertext = line.substring(3).trim();
+ }
+ else if (line.startsWith("test="))
+ {
+ if ((key != null) && (plaintext != null)
+ && (ciphertext != null))
+ {
+ tests.add(new BlockCipherVectorTest(testNum++,
+ createNewEngineForTest(), new KeyParameter(Hex
+ .decode(key)), plaintext, ciphertext));
+ }
+ }
+
+ line = in.readLine();
+ }
+ try
+ {
+ in.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ catch (IOException e)
+ {
+ }
+ if ((key != null) && (plaintext != null) && (ciphertext != null))
+ {
+ tests.add(new BlockCipherVectorTest(testNum++,
+ createNewEngineForTest(),
+ new KeyParameter(Hex.decode(key)), plaintext, ciphertext));
+ }
+ return (Test[])(tests.toArray(new Test[tests.size()]));
+ }
+
+ public String getName()
+ {
+ return "AES";
+ }
+
+ private TestResult performTestsFromZipFile(File zfile)
+ {
+ try
+ {
+ ZipFile inZip = new ZipFile(zfile);
+ for (Enumeration files = inZip.entries(); files.hasMoreElements();)
+ {
+ Test[] tests = null;
+ try
+ {
+ tests = readTestVectors(inZip
+ .getInputStream((ZipEntry)(files.nextElement())));
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": threw "
+ + e);
+ }
+ if (tests != null)
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult res = tests[i].perform();
+ countOfTests++;
+
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+ }
+ }
+ }
+ inZip.close();
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": threw " + e);
+ }
+ }
+
+ private static final String[] zipFileNames = { "rijn.tv.ecbnk.zip",
+ "rijn.tv.ecbnt.zip", "rijn.tv.ecbvk.zip", "rijn.tv.ecbvt.zip" };
+
+ public TestResult perform()
+ {
+ countOfTests = 0;
+ for (int i = 0; i < zipFileNames.length; i++)
+ {
+ File inf = new File(zipFileNames[i]);
+ TestResult res = performTestsFromZipFile(inf);
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+ }
+ return new SimpleTestResult(true, getName() + ": " + countOfTests
+ + " performed Okay");
+ }
+
+ public static void main(String[] args)
+ {
+ AESVectorFileTest test = new AESVectorFileTest();
+ TestResult result = test.perform();
+ System.out.println(result);
+
+ test = new AESLightVectorFileTest();
+ result = test.perform();
+ System.out.println(result);
+
+ test = new AESFastVectorFileTest();
+ result = test.perform();
+ System.out.println(result);
+
+ }
+
+ private static class AESLightVectorFileTest extends AESVectorFileTest
+ {
+ protected BlockCipher createNewEngineForTest()
+ {
+ return new AESLightEngine();
+ }
+
+ public String getName()
+ {
+ return "AESLight";
+ }
+
+ }
+
+ private static class AESFastVectorFileTest extends AESVectorFileTest
+ {
+ protected BlockCipher createNewEngineForTest()
+ {
+ return new AESFastEngine();
+ }
+
+ public String getName()
+ {
+ return "AESFast";
+ }
+
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/AESWrapPadTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESWrapPadTest.java
new file mode 100644
index 00000000..75ac5255
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESWrapPadTest.java
@@ -0,0 +1,146 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.engines.AESWrapPadEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * This is a test harness I use because I cannot modify the BC test harness without
+ * invalidating the signature on their signed provider library. The code here is not
+ * high quality but it does test the RFC vectors as well as randomly generated values.
+ * The RFC test vectors are tested by making sure both the ciphertext and decrypted
+ * values match the expected values whereas the random values are just checked to make
+ * sure that:
+ * <p>unwrap(wrap(random_value, random_kek), random_kek) == random_value.</p>
+ */
+
+public class AESWrapPadTest
+ extends SimpleTest
+{
+
+ private final int numOfRandomIterations = 100;
+
+ public AESWrapPadTest()
+ {
+
+ }
+
+ private void wrapAndUnwrap(byte[] kek, byte[] key, byte[] expected)
+ throws Exception
+ {
+ Wrapper wrapper = new AESWrapPadEngine();
+
+ wrapper.init(true, new KeyParameter(kek));
+
+ byte[] cipherText = wrapper.wrap(key, 0, key.length);
+ if (!areEqual(cipherText, expected))
+ {
+ fail("Wrapped value does not match expected.");
+ }
+ wrapper.init(false, new KeyParameter(kek));
+ byte[] plainText = wrapper.unwrap(cipherText, 0, cipherText.length);
+
+ if (!areEqual(key, plainText))
+ {
+ fail("Unwrapped value does not match original.");
+ }
+ }
+
+ private void wrapAndUnwrap(byte[] kek, byte[] key)
+ throws Exception
+ {
+ Wrapper wrapper = new AESWrapPadEngine();
+
+ wrapper.init(true, new KeyParameter(kek));
+
+ byte[] cipherText = wrapper.wrap(key, 0, key.length);
+
+ wrapper.init(false, new KeyParameter(kek));
+ byte[] plainText = wrapper.unwrap(cipherText, 0, cipherText.length);
+
+ if (!areEqual(key, plainText))
+ {
+ fail("Unwrapped value does not match original.");
+ }
+ }
+
+ public String getName()
+ {
+ return "AESWrapPad";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ // test RFC 5649 test vectors
+ byte[] kek = Hex.decode("5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8");
+ byte[] key = Hex.decode("c37b7e6492584340bed12207808941155068f738");
+ byte[] wrap = Hex.decode("138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a");
+
+ wrapAndUnwrap(kek, key, wrap);
+
+ wrap = Hex.decode("afbeb0f07dfbf5419200f2ccb50bb24f");
+ key = Hex.decode("466f7250617369");
+ wrapAndUnwrap(kek, key, wrap);
+
+
+ //
+ // offset test
+ //
+ Wrapper wrapper = new AESWrapPadEngine();
+
+ byte[] pText = new byte[5 + key.length];
+ byte[] cText;
+
+ System.arraycopy(key, 0, pText, 5, key.length);
+
+ wrapper.init(true, new KeyParameter(kek));
+
+ cText = wrapper.wrap(pText, 5, key.length);
+ if (!Arrays.areEqual(cText, wrap))
+ {
+ fail("failed offset wrap test expected " + new String(Hex.encode(wrap)) + " got " + new String(Hex.encode(cText)));
+ }
+
+ wrapper.init(false, new KeyParameter(kek));
+
+ cText = new byte[6 + wrap.length];
+ System.arraycopy(wrap, 0, cText, 6, wrap.length);
+
+ pText = wrapper.unwrap(cText, 6, wrap.length);
+ if (!Arrays.areEqual(pText, key))
+ {
+ fail("failed offset unwrap test expected " + new String(Hex.encode(key)) + " got " + new String(Hex.encode(pText)));
+ }
+
+ // test random values
+ SecureRandom rnd = new SecureRandom();
+ for (int i = 0; i < numOfRandomIterations; i++)
+ {
+ int kekLength = 128;
+ boolean shouldIncrease = (rnd.nextInt() & 0x01) != 0;
+ if (shouldIncrease)
+ {
+ kekLength = 256;
+ }
+ kek = new byte[kekLength / 8];
+ rnd.nextBytes(kek);
+ int keyToWrapSize = rnd.nextInt(256 / 8 - 8) + 8;
+ byte[] keyToWrap = new byte[keyToWrapSize];
+ rnd.nextBytes(keyToWrap);
+ wrapAndUnwrap(kek, keyToWrap);
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new AESWrapPadTest());
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/AESWrapTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESWrapTest.java
new file mode 100644
index 00000000..7bd1e4b4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/AESWrapTest.java
@@ -0,0 +1,260 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.engines.AESWrapEngine;
+import org.bouncycastle.crypto.engines.AESWrapPadEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestFailedException;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * Wrap Test
+ */
+public class AESWrapTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "AESWrap";
+ }
+
+ private void wrapTest(
+ int id,
+ byte[] kek,
+ byte[] in,
+ byte[] out)
+ {
+ wrapTest(id, kek, in, out, false);
+ }
+
+ private void wrapTest(
+ int id,
+ byte[] kek,
+ byte[] in,
+ byte[] out,
+ boolean useReverseDirection)
+ {
+ Wrapper wrapper = new AESWrapEngine(useReverseDirection);
+
+ wrapper.init(true, new KeyParameter(kek));
+
+ try
+ {
+ byte[] cText = wrapper.wrap(in, 0, in.length);
+ if (!Arrays.areEqual(cText, out))
+ {
+ fail("failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+ }
+ catch (TestFailedException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ fail("failed wrap test exception " + e.toString());
+ }
+
+ wrapper.init(false, new KeyParameter(kek));
+
+ try
+ {
+ byte[] pText = wrapper.unwrap(out, 0, out.length);
+ if (!Arrays.areEqual(pText, in))
+ {
+ fail("failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+ catch (TestFailedException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ fail("failed unwrap test exception.", e);
+ }
+
+ //
+ // offset test
+ //
+ byte[] pText = new byte[5 + in.length];
+ byte[] cText;
+
+ System.arraycopy(in, 0, pText, 5, in.length);
+
+ wrapper.init(true, new KeyParameter(kek));
+
+ try
+ {
+ cText = wrapper.wrap(pText, 5, in.length);
+ if (!Arrays.areEqual(cText, out))
+ {
+ fail("failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+ }
+ catch (Exception e)
+ {
+ fail("failed wrap test exception " + e.toString());
+ }
+
+ wrapper.init(false, new KeyParameter(kek));
+
+ cText = new byte[6 + out.length];
+ System.arraycopy(out, 0, cText, 6, out.length);
+
+ try
+ {
+ pText = wrapper.unwrap(cText, 6, out.length);
+ if (!Arrays.areEqual(pText, in))
+ {
+ fail("failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+ catch (Exception e)
+ {
+ fail("failed unwrap test exception.", e);
+ }
+ }
+
+ private void heapIssueTest()
+ {
+ byte[] key = Hex.decode("d305ef52a6b9e72c810b821261d2d678");
+ byte[] ciphertext = Hex.decode("d2b2906d209a46261d8f6794eca3179d");
+
+ Wrapper aes = new AESWrapPadEngine();
+ aes.init(false, new KeyParameter(key));
+ try
+ {
+ byte[] result = aes.unwrap(ciphertext, 0, ciphertext.length);
+
+ fail("incorrect pad not detected");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // ignore
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ byte[] kek1 = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] in1 = Hex.decode("00112233445566778899aabbccddeeff");
+ byte[] out1 = Hex.decode("1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5");
+
+ wrapTest(1, kek1, in1, out1);
+
+ byte[] kek2 = Hex.decode("000102030405060708090a0b0c0d0e0f1011121314151617");
+ byte[] in2 = Hex.decode("00112233445566778899aabbccddeeff");
+ byte[] out2 = Hex.decode("96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d");
+
+ wrapTest(2, kek2, in2, out2);
+
+ byte[] kek3 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ byte[] in3 = Hex.decode("00112233445566778899aabbccddeeff");
+ byte[] out3 = Hex.decode("64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7");
+
+ wrapTest(3, kek3, in3, out3);
+
+ byte[] kek4 = Hex.decode("000102030405060708090a0b0c0d0e0f1011121314151617");
+ byte[] in4 = Hex.decode("00112233445566778899aabbccddeeff0001020304050607");
+ byte[] out4 = Hex.decode("031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2");
+ wrapTest(4, kek4, in4, out4);
+
+ byte[] kek5 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ byte[] in5 = Hex.decode("00112233445566778899aabbccddeeff0001020304050607");
+ byte[] out5 = Hex.decode("a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1");
+ wrapTest(5, kek5, in5, out5);
+
+ byte[] kek6 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ byte[] in6 = Hex.decode("00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f");
+ byte[] out6 = Hex.decode("28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21");
+ wrapTest(6, kek6, in6, out6);
+
+ byte[] kek7 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ byte[] in7 = Hex.decode("00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f");
+ byte[] out7 = Hex.decode("cba01acbdb4c7c39fa59babb383c485f318837208731a81c735b5be6ba710375a1159e26a9b57228");
+ wrapTest(7, kek7, in7, out7, true);
+
+ Wrapper wrapper = new AESWrapEngine();
+ KeyParameter key = new KeyParameter(new byte[16]);
+ byte[] buf = new byte[16];
+
+ try
+ {
+ wrapper.init(true, key);
+
+ wrapper.unwrap(buf, 0, buf.length);
+
+ fail("failed unwrap state test.");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("unexpected exception: " + e, e);
+ }
+
+ try
+ {
+ wrapper.init(false, key);
+
+ wrapper.wrap(buf, 0, buf.length);
+
+ fail("failed unwrap state test.");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+
+ //
+ // short test
+ //
+ try
+ {
+ wrapper.init(false, key);
+
+ wrapper.unwrap(buf, 0, buf.length / 2);
+
+ fail("failed unwrap short test.");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ wrapper.init(true, key);
+
+ wrapper.wrap(buf, 0, 15);
+
+ fail("ailed wrap length test.");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ heapIssueTest();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ AESWrapTest test = new AESWrapTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/AllTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/AllTests.java
new file mode 100644
index 00000000..35bf2350
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/AllTests.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.bouncycastle.util.test.SimpleTestResult;
+
+public class AllTests
+ extends TestCase
+{
+ public void testCrypto()
+ {
+ org.bouncycastle.util.test.Test[] tests = RegressionTest.tests;
+
+ for (int i = 0; i != tests.length; i++)
+ {
+ SimpleTestResult result = (SimpleTestResult)tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ fail(result.toString());
+ }
+ }
+ }
+
+ public static void main (String[] args)
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ {
+ TestSuite suite = new TestSuite("Lightweight Crypto Tests");
+
+ suite.addTestSuite(AllTests.class);
+ suite.addTestSuite(GCMReorderTest.class);
+
+ return suite;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/BCryptTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/BCryptTest.java
new file mode 100644
index 00000000..6a4f777e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/BCryptTest.java
@@ -0,0 +1,151 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.generators.BCrypt;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/*
+ * bcrypt test vectors
+ */
+public class BCryptTest
+ extends SimpleTest
+{
+ // Raw test vectors based on crypt style test vectors
+ // Cross checked with JBCrypt
+ private static final Object[][] testVectors = {
+ {"", "144b3d691a7b4ecf39cf735c7fa7a79c", Integers.valueOf(6), "557e94f34bf286e8719a26be94ac1e16d95ef9f819dee092"},
+ {"00", "144b3d691a7b4ecf39cf735c7fa7a79c", Integers.valueOf(6), "557e94f34bf286e8719a26be94ac1e16d95ef9f819dee092"},
+ {"00", "26c63033c04f8bcba2fe24b574db6274", Integers.valueOf(8), "56701b26164d8f1bc15225f46234ac8ac79bf5bc16bf48ba"},
+ {"00", "9b7c9d2ada0fd07091c915d1517701d6", Integers.valueOf(10), "7b2e03106a43c9753821db688b5cc7590b18fdf9ba544632"},
+ {"6100", "a3612d8c9a37dac2f99d94da03bd4521", Integers.valueOf(6), "e6d53831f82060dc08a2e8489ce850ce48fbf976978738f3"},
+ {"6100", "7a17b15dfe1c4be10ec6a3ab47818386", Integers.valueOf(8), "a9f3469a61cbff0a0f1a1445dfe023587f38b2c9c40570e1"},
+ {"6100", "9bef4d04e1f8f92f3de57323f8179190", Integers.valueOf(10), "5169fd39606d630524285147734b4c981def0ee512c3ace1"},
+ {"61626300", "2a1f1dc70a3d147956a46febe3016017", Integers.valueOf(6), "d9a275b493bcbe1024b0ff80d330253cfdca34687d8f69e5"},
+ {"61626300", "4ead845a142c9bc79918c8797f470ef5", Integers.valueOf(8), "8d4131a723bfbbac8a67f2e035cae08cc33b69f37331ea91"},
+ {"61626300", "631c554493327c32f9c26d9be7d18e4c", Integers.valueOf(10), "8cd0b863c3ff0860e31a2b42427974e0283b3af7142969a6"},
+ {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "02d1176d74158ee29cffdac6150cf123", Integers.valueOf(6), "4d38b523ce9dc6f2f6ff9fb3c2cd71dfe7f96eb4a3baf19f"},
+ {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "715b96caed2ac92c354ed16c1e19e38a", Integers.valueOf(8), "98bf9ffc1f5be485f959e8b1d526392fbd4ed2d5719f506b"},
+ {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "85727e838f9049397fbec90566ede0df", Integers.valueOf(10), "cebba53f67bd28af5a44c6707383c231ac4ef244a6f5fb2b"},
+ {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "8512ae0d0fac4ec9a5978f79b6171028", Integers.valueOf(6), "26f517fe5345ad575ba7dfb8144f01bfdb15f3d47c1e146a"},
+ {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "1ace2de8807df18c79fced54678f388f", Integers.valueOf(8), "d51d7cdf839b91a25758b80141e42c9f896ae80fd6cd561f"},
+ {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "36285a6267751b14ba2dc989f6d43126", Integers.valueOf(10), "db4fab24c1ff41c1e2c966f8b3d6381c76e86f52da9e15a9"},
+ {"c2a300", "144b3d691a7b4ecf39cf735c7fa7a79c", Integers.valueOf(6), "5a6c4fedb23980a7da9217e0442565ac6145b687c7313339"},
+ };
+
+ public String getName()
+ {
+ return "BCrypt";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testParameters();
+ testShortKeys();
+ testVectors();
+ }
+
+ private void testShortKeys()
+ {
+ byte[] salt = new byte[16];
+
+ // Check BCrypt with empty key pads to zero byte key
+ byte[] hashEmpty = BCrypt.generate(new byte[0], salt, 4);
+ byte[] hashZero1 = BCrypt.generate(new byte[1], salt, 4);
+
+ if (!Arrays.areEqual(hashEmpty, hashZero1))
+ {
+ fail("Hash for empty password should equal zeroed key", new String(Hex.encode(hashEmpty)),
+ new String(Hex.encode(hashZero1)));
+ }
+
+ // Check zeroed byte key of min Blowfish length is equivalent
+ byte[] hashZero4 = BCrypt.generate(new byte[4], salt, 4);
+ if (!Arrays.areEqual(hashEmpty, hashZero4))
+ {
+ fail("Hash for empty password should equal zeroed key[4]", new String(Hex.encode(hashEmpty)), new String(
+ Hex.encode(hashZero4)));
+ }
+
+ // Check BCrypt isn't padding too small (32 bit) keys
+ byte[] hashA = BCrypt.generate(new byte[]{(byte)'a'}, salt, 4);
+ byte[] hashA0 = BCrypt.generate(new byte[]{(byte)'a', (byte)0}, salt, 4);
+ if (Arrays.areEqual(hashA, hashA0))
+ {
+ fail("Small keys should not be 0 padded.");
+ }
+ }
+
+ public void testParameters()
+ {
+ checkOK("Empty key", new byte[0], new byte[16], 4);
+ checkOK("Minimal values", new byte[1], new byte[16], 4);
+ // checkOK("Max cost", new byte[1], new byte[16], 31);
+ checkOK("Max passcode", new byte[72], new byte[16], 4);
+ checkIllegal("Null password", null, new byte[16], 4);
+ checkIllegal("Null salt", new byte[1], null, 4);
+ checkIllegal("Salt too small", new byte[1], new byte[15], 4);
+ checkIllegal("Salt too big", new byte[1], new byte[17], 4);
+ checkIllegal("Cost too low", new byte[16], new byte[16], 3);
+ checkIllegal("Cost too high", new byte[16], new byte[16], 32);
+ checkIllegal("Passcode too long", new byte[73], new byte[16], 32);
+ }
+
+ private void checkOK(String msg, byte[] pass, byte[] salt, int cost)
+ {
+ try
+ {
+ BCrypt.generate(pass, salt, cost);
+ }
+ catch (IllegalArgumentException e)
+ {
+ e.printStackTrace();
+ fail(msg);
+ }
+ }
+
+ private void checkIllegal(String msg, byte[] pass, byte[] salt, int cost)
+ {
+ try
+ {
+ BCrypt.generate(pass, salt, cost);
+ fail(msg);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // e.printStackTrace();
+ }
+ }
+
+ public void testVectors()
+ throws Exception
+ {
+ for (int i = 0; i < testVectors.length; i++)
+ {
+ byte[] password = Hex.decode((String)testVectors[i][0]);
+ byte[] salt = Hex.decode((String)testVectors[i][1]);
+ int cost = ((Integer)testVectors[i][2]).intValue();
+ byte[] expected = Hex.decode((String)testVectors[i][3]);
+
+ test(password, salt, cost, expected);
+ }
+
+ }
+
+ private void test(byte[] password, byte[] salt, int cost, byte[] expected)
+ {
+ byte[] hash = BCrypt.generate(password, salt, cost);
+ if (!Arrays.areEqual(hash, expected))
+ {
+ fail("Hash for " + new String(Hex.encode(password)), new String(Hex.encode(expected)),
+ new String(Hex.encode(hash)));
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new BCryptTest());
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/BigSkippingCipherTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/BigSkippingCipherTest.java
new file mode 100644
index 00000000..695a0095
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/BigSkippingCipherTest.java
@@ -0,0 +1,223 @@
+package org.bouncycastle.crypto.test;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.SkippingStreamCipher;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.ChaChaEngine;
+import org.bouncycastle.crypto.engines.Salsa20Engine;
+import org.bouncycastle.crypto.modes.SICBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+
+public class BigSkippingCipherTest
+ extends TestCase
+{
+ public void testAESCTR()
+ throws Exception
+ {
+ CipherParameters params = new ParametersWithIV(new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")), Hex.decode("00000000000000000000000000000000"));
+ SICBlockCipher linearEngine = new SICBlockCipher(new AESEngine());
+ SICBlockCipher skippingEngine = new SICBlockCipher(new AESEngine());
+ Random random = new Random();
+
+ linearEngine.init(true, params);
+ skippingEngine.init(false, params);
+
+ testCipher(random, linearEngine, skippingEngine);
+
+ byte[] in = Base64.decode("pzVbCyj5JntIYN2Kvzf64/po+gTu/jvnZwU33F7UsfxpWRUDEGIQbArxqCQzEkGAwa4omXJ28WJveJNUQbQ5cBxS2aTt3sV0mrP+cneJ3OZkzo5Lhz0vuXs7Mav9uUzQFrU0DuMyGr1QJnKO0BUal0gLJ0v6YAo2SObDS5A4CTrsAgo0C2UXQmnuGzyYlhm4VoSNotD8auqyQXUrT8c2B/tLIcjyyl8ug1BabL2gAxN7oKbpvW5j2z3IZNZh+AKR4OR47RfjtYglOfgGQB1L5yiL9reuWEsRjbZmcEFfLmAAK0gtcP+0KDXV/DS4NxRx0sC7NkBzSC3uq2RbQOdjygFZ5qrmvwQZgLqlXs2cGiNzqx5CRjAvl83aAWAAerR5T2sHadRckW01oE2ivQixpgdPCGFHLeXoPMlkZ/r1cECYFAGPjMGSG2qnQZ/ZJbStEAZye/11r/dyoUgP+XTc6FDCCQHSdmyQljPHDQm7ioMTTkCf15YWv0kSuKOd4nBlLrGjSl9dsJsVU9TDRCqNExmk+lN3f0p8vr8TVNnir+OtEabOXzjOv6i3PHQZP27ML3Hy9TU1MHx1Q0bLdgi6yIw8lmzzBzok8j5VrCpNf2HmBqtRm1WDeTO9R62OWyaMT/dCT7AzEzP8ClBSq57p69OrlEDoaLXrYvNS3hEw1Lo=");
+ byte[] exp = Base64.decode("JD0KPwMaKvqYZnDgI0rFTCbexg+RRj3UbEPtsf5IDeM3lb5OJ5EMLHXrfknu//XNLE6dV/Jaoz3LuylkfRlMg2/Vvgo6KwXNV3VsUgkEmTpy74NAd9DCh+1EgJYCNbHkT/haaKpPWLqHEIp1/LVKZZgRXc+C4kH02GzqwYkjUZCSrE8GBpiILlHhN+2A14Ltmxe7XZrlnOQx62sBoh+QR2ZCSAhQjMayhnkrIC1qpM8S4vcZbAYOGuVRcmBhGZdvQg+YXUKrx3FS6XS+xF3yMld5iVx2aEzjkmXuDoULLzrppD/5Ed0I2CCFZigBZ3ZFsOGIZw1yTBXLRVroM/xksYaGgQs9arKB3rEBpUV4yUMuYokqz9A4k7jg/6loTFU8SntBzXptrPuKPbEMT/FvJqEsI3yKCndYiRAkTRmHWhNmdjLH9Pw32VYlpbjYzQjPP6Iy055VujucBofsP3/ENK32XNs1I6PzcVrjjRJaBy3dbAB0e7/P7cqnUvKu+dSR7N92VnuDtCaS5ksVMWJOlfLhqLq8umz8+3aIsTj20bfNGzf4aeqkUzv+AezQedVY3gmYBgwv/1ZR3Y9bUATE4ieIwK+gkZBgtANOs2abY+8+of5sQhyfYiWpUrSb+L/7MjaFgBz+b69bD5xl0kes/ySVGBqaqG4jcOr01qGfaLw=");
+ byte[] buf = new byte[512];
+
+ skippingEngine.seekTo(1L << 38);
+
+ skippingEngine.processBytes(in, 0, in.length, buf, 0);
+
+ if (!Arrays.areEqual(buf, exp))
+ {
+ fail("long seek failed");
+ }
+
+ skippingEngine.skip(-(1L << 38) - 512);
+
+ if (skippingEngine.getPosition() != 0)
+ {
+ fail("zero position came back as: " + skippingEngine.getPosition());
+ }
+
+ random.nextBytes(buf);
+
+ byte[] linOut = new byte[512];
+ byte[] skipOut = new byte[512];
+
+ linearEngine.init(true, params);
+
+ linearEngine.processBytes(buf, 0, buf.length, linOut, 0);
+ skippingEngine.processBytes(buf, 0, buf.length, skipOut, 0);
+
+ if (!Arrays.areEqual(linOut, skipOut))
+ {
+ fail("long output mismatch");
+ }
+ }
+
+ public void testSalsa20()
+ throws Exception
+ {
+ CipherParameters params = new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE"));
+ Salsa20Engine linearEngine = new Salsa20Engine();
+ Salsa20Engine skippingEngine = new Salsa20Engine();
+ Random random = new Random();
+
+ linearEngine.init(true, params);
+ skippingEngine.init(false, params);
+
+ testCipher(random, linearEngine, skippingEngine);
+
+ byte[] in = Base64.decode("pzVbCyj5JntIYN2Kvzf64/po+gTu/jvnZwU33F7UsfxpWRUDEGIQbArxqCQzEkGAwa4omXJ28WJveJNUQbQ5cBxS2aTt3sV0mrP+cneJ3OZkzo5Lhz0vuXs7Mav9uUzQFrU0DuMyGr1QJnKO0BUal0gLJ0v6YAo2SObDS5A4CTrsAgo0C2UXQmnuGzyYlhm4VoSNotD8auqyQXUrT8c2B/tLIcjyyl8ug1BabL2gAxN7oKbpvW5j2z3IZNZh+AKR4OR47RfjtYglOfgGQB1L5yiL9reuWEsRjbZmcEFfLmAAK0gtcP+0KDXV/DS4NxRx0sC7NkBzSC3uq2RbQOdjygFZ5qrmvwQZgLqlXs2cGiNzqx5CRjAvl83aAWAAerR5T2sHadRckW01oE2ivQixpgdPCGFHLeXoPMlkZ/r1cECYFAGPjMGSG2qnQZ/ZJbStEAZye/11r/dyoUgP+XTc6FDCCQHSdmyQljPHDQm7ioMTTkCf15YWv0kSuKOd4nBlLrGjSl9dsJsVU9TDRCqNExmk+lN3f0p8vr8TVNnir+OtEabOXzjOv6i3PHQZP27ML3Hy9TU1MHx1Q0bLdgi6yIw8lmzzBzok8j5VrCpNf2HmBqtRm1WDeTO9R62OWyaMT/dCT7AzEzP8ClBSq57p69OrlEDoaLXrYvNS3hEw1Lo=");
+ byte[] exp = Base64.decode("e0bdyXVHsxzA9pZ/htVVPAsAgief6pEyLmdayG09N3GkBZFulTze/He524ETzTGtV7c1yGypTwjwVr+rNWmZs9YeXtYljySAQUbv1il5spmn7+iiwN6H21Keg6r4ciwzR7jhm7Wc0A1GGkh8OLmb2ZAh/fNDXHyL8mbEmLYh5C9n+DCTruTEjtS5TwueaRsUSNkexUgemqOVHxeOD0nZcVARr2AzMW6btNrQycol3+WTvLmbCeAZwcZnfPvZeU3r2UF73o8lP0vOUrOi095H2WZkJIVrAiV/+i4Sb76XXRgFlvWP6RbX9mYApIBhs69+yxp8lmVI0AABAwwV7PNXo+1UK6kzNi5spa32MRDMogP+wDHMyu8nHzLpIv9OTx0CmkZ0XO4Lla3d3UsPGq8g50a6gfrSOa9JHYFjfMzqIY/6SdZxr39Z8jVYiCfWGYplMTSDvfj3whk2J0DnSSdf6k6JstCjIXeMagKjwpcf9r0kq1Q9mAGhdJLqkM2LYHz3CP6GiWbGy5477GKnrhFDOeG1PtLv4YaTLrrjnNngIeeMK0tgkwBhsobVCD1hSs26I9/V+rdFhFb3/a/ob37cfnPmflbC0oOpSKoY6tZEaDp9u2ulNCpLYV6zrn3k9soP4q+sfsmXKMuWU2+rJGvBOEPh9Jo8Z+u7r+1PG+8VgAs=");
+ byte[] buf = new byte[512];
+
+ skippingEngine.seekTo(1L << 38);
+
+ skippingEngine.processBytes(in, 0, in.length, buf, 0);
+
+ if (!Arrays.areEqual(buf, exp))
+ {
+ fail("long seek failed");
+ }
+
+ skippingEngine.skip(-(1L << 38) - 512);
+
+ if (skippingEngine.getPosition() != 0)
+ {
+ fail("zero position came back as: " + skippingEngine.getPosition());
+ }
+
+ random.nextBytes(buf);
+
+ byte[] linOut = new byte[512];
+ byte[] skipOut = new byte[512];
+
+ linearEngine.init(true, params);
+
+ linearEngine.processBytes(buf, 0, buf.length, linOut, 0);
+ skippingEngine.processBytes(buf, 0, buf.length, skipOut, 0);
+
+ if (!Arrays.areEqual(linOut, skipOut))
+ {
+ fail("long output mismatch");
+ }
+ }
+
+ public void testChaCha()
+ throws Exception
+ {
+ CipherParameters params = new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE"));
+ ChaChaEngine linearEngine = new ChaChaEngine();
+ ChaChaEngine skippingEngine = new ChaChaEngine();
+ Random random = new Random();
+
+ linearEngine.init(true, params);
+ skippingEngine.init(false, params);
+
+ testCipher(random, linearEngine, skippingEngine);
+
+ byte[] in = Base64.decode("pzVbCyj5JntIYN2Kvzf64/po+gTu/jvnZwU33F7UsfxpWRUDEGIQbArxqCQzEkGAwa4omXJ28WJveJNUQbQ5cBxS2aTt3sV0mrP+cneJ3OZkzo5Lhz0vuXs7Mav9uUzQFrU0DuMyGr1QJnKO0BUal0gLJ0v6YAo2SObDS5A4CTrsAgo0C2UXQmnuGzyYlhm4VoSNotD8auqyQXUrT8c2B/tLIcjyyl8ug1BabL2gAxN7oKbpvW5j2z3IZNZh+AKR4OR47RfjtYglOfgGQB1L5yiL9reuWEsRjbZmcEFfLmAAK0gtcP+0KDXV/DS4NxRx0sC7NkBzSC3uq2RbQOdjygFZ5qrmvwQZgLqlXs2cGiNzqx5CRjAvl83aAWAAerR5T2sHadRckW01oE2ivQixpgdPCGFHLeXoPMlkZ/r1cECYFAGPjMGSG2qnQZ/ZJbStEAZye/11r/dyoUgP+XTc6FDCCQHSdmyQljPHDQm7ioMTTkCf15YWv0kSuKOd4nBlLrGjSl9dsJsVU9TDRCqNExmk+lN3f0p8vr8TVNnir+OtEabOXzjOv6i3PHQZP27ML3Hy9TU1MHx1Q0bLdgi6yIw8lmzzBzok8j5VrCpNf2HmBqtRm1WDeTO9R62OWyaMT/dCT7AzEzP8ClBSq57p69OrlEDoaLXrYvNS3hEw1Lo=");
+ byte[] exp = Base64.decode("FACFDKSYxFYEOknCBPdfy5elbrDu8FzOImwpczlIk1HWlcbBPHXwHEnVaKrGtmthC7gA1DQJSeobO83KW3YZVkT8fcGnMFbeee6ISs9R4KqekE+Fs8uNWYlqsgT5xrErOC/cmz4B5envQx7EZK5h+fJupYO3vHqVk5/Q6c/v8ndDeBKSDTKA6eyybOwFVIjwJKPfuliu4mJGHphUIsp/OgRPs+VhlMrWXMVwsGzGHy9xZvTz6Xv6GJvrIoONMHh24YGOSt+83cFTepU7ur8anyDaoWzMz/n04eopnQd9TlREwYOZWdF0ZAJ0VZQYEixopmH+mlEZ/Nyw6IDswvyX3Zf/7lyDsM8bv2kz1gXvmQgUMqr6wXrOuJtxaH8aUvLVswCeNZEGFl17FHgwdD2MRzkmhfPRFlTgicd02D/ateBs5B0ORu5CKu3p/RGjU4YE68ONPNEkwkBRm5uGzdezTJmUzdJAEtoIxv1XfE1tytP7U+BpWdP5LY5NlEUo6sNR4O2nlSQJkAOzhoz821hnn1IL6r9DLDHIW40IhStDqc5Hy/8rEZgnnFhIE6pAD1PAGV5oJk/Z/V64bFvGkpD7xuhN5U2Eic7UheB8D227JtQQWTc8GhynlOWbmkYm/koKw+ieraN5IWE/KD2HFqJhxasB9lb3lMGh3zfgBKck5Lo=");
+ byte[] buf = new byte[512];
+
+ skippingEngine.seekTo(1L << 38);
+
+ skippingEngine.processBytes(in, 0, in.length, buf, 0);
+
+ if (!Arrays.areEqual(buf, exp))
+ {
+ fail("long seek failed");
+ }
+
+ skippingEngine.skip(-(1L << 38) - 512);
+
+ if (skippingEngine.getPosition() != 0)
+ {
+ fail("zero position came back as: " + skippingEngine.getPosition());
+ }
+
+ random.nextBytes(buf);
+
+ byte[] linOut = new byte[512];
+ byte[] skipOut = new byte[512];
+
+ linearEngine.init(true, params);
+
+ linearEngine.processBytes(buf, 0, buf.length, linOut, 0);
+ skippingEngine.processBytes(buf, 0, buf.length, skipOut, 0);
+
+ if (!Arrays.areEqual(linOut, skipOut))
+ {
+ fail("long output mismatch");
+ }
+ }
+
+ public void testCipher(Random random, SkippingStreamCipher linearEngine, SkippingStreamCipher skippingEngine)
+ throws Exception
+ {
+ byte[] startDataBuf = new byte[1 << 16];
+ byte[] startEncBuf = new byte[1 << 16];
+ byte[] startSeekBuf = new byte[1 << 16];
+
+ random.nextBytes(startDataBuf);
+
+ linearEngine.processBytes(startDataBuf, 0, startDataBuf.length, startEncBuf, 0);
+
+ byte[] incBuf = new byte[1 << 12];
+ byte[] linearOutBuf = new byte[1 << 12];
+ byte[] seekOutBuf = new byte[1 << 12];
+
+ for (long i = 0; i != 1L << 20; i++)
+ {
+ random.nextBytes(incBuf);
+
+ linearEngine.processBytes(incBuf, 0, incBuf.length, linearOutBuf, 0);
+
+ skippingEngine.seekTo(startDataBuf.length + i * incBuf.length);
+
+ if (skippingEngine.getPosition() != startDataBuf.length + i * incBuf.length)
+ {
+ fail(i + "th position came back as: " + skippingEngine.getPosition());
+ }
+
+ skippingEngine.processBytes(incBuf, 0, incBuf.length, seekOutBuf, 0);
+
+ if (!Arrays.areEqual(linearOutBuf, seekOutBuf))
+ {
+ fail("output mismatch");
+ }
+
+ if (skippingEngine.getPosition() != startDataBuf.length + (i + 1) * incBuf.length)
+ {
+ fail(i + "th + 1 position came back as: " + skippingEngine.getPosition());
+ }
+
+ skippingEngine.skip(-skippingEngine.getPosition());
+
+ if (skippingEngine.getPosition() != 0)
+ {
+ fail("zero position came back as: " + skippingEngine.getPosition());
+ }
+
+ skippingEngine.processBytes(startEncBuf, 0, startEncBuf.length, startSeekBuf, 0);
+
+ if (!Arrays.areEqual(startDataBuf, startSeekBuf))
+ {
+ fail("output mismatch");
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherMonteCarloTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherMonteCarloTest.java
new file mode 100644
index 00000000..72242c58
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherMonteCarloTest.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * a basic test that takes a cipher, key parameter, and an input
+ * and output string. This test wraps the engine in a buffered block
+ * cipher with padding disabled.
+ */
+public class BlockCipherMonteCarloTest
+ extends SimpleTest
+{
+ int id;
+ int iterations;
+ BlockCipher engine;
+ CipherParameters param;
+ byte[] input;
+ byte[] output;
+
+ public BlockCipherMonteCarloTest(
+ int id,
+ int iterations,
+ BlockCipher engine,
+ CipherParameters param,
+ String input,
+ String output)
+ {
+ this.id = id;
+ this.iterations = iterations;
+ this.engine = engine;
+ this.param = param;
+ this.input = Hex.decode(input);
+ this.output = Hex.decode(output);
+ }
+
+ public String getName()
+ {
+ return engine.getAlgorithmName() + " Monte Carlo Test " + id;
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BufferedBlockCipher cipher = new BufferedBlockCipher(engine);
+
+ cipher.init(true, param);
+
+ byte[] out = new byte[input.length];
+
+ System.arraycopy(input, 0, out, 0, out.length);
+
+ for (int i = 0; i != iterations; i++)
+ {
+ int len1 = cipher.processBytes(out, 0, out.length, out, 0);
+
+ cipher.doFinal(out, len1);
+ }
+
+ if (!areEqual(out, output))
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ cipher.init(false, param);
+
+ for (int i = 0; i != iterations; i++)
+ {
+ int len1 = cipher.processBytes(out, 0, out.length, out, 0);
+
+ cipher.doFinal(out, len1);
+ }
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherResetTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherResetTest.java
new file mode 100644
index 00000000..a35835b0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherResetTest.java
@@ -0,0 +1,206 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.AESLightEngine;
+import org.bouncycastle.crypto.engines.BlowfishEngine;
+import org.bouncycastle.crypto.engines.CAST5Engine;
+import org.bouncycastle.crypto.engines.CAST6Engine;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.NoekeonEngine;
+import org.bouncycastle.crypto.engines.RC6Engine;
+import org.bouncycastle.crypto.engines.SEEDEngine;
+import org.bouncycastle.crypto.engines.SerpentEngine;
+import org.bouncycastle.crypto.engines.TEAEngine;
+import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.engines.XTEAEngine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.GOFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher;
+import org.bouncycastle.crypto.modes.PGPCFBBlockCipher;
+import org.bouncycastle.crypto.modes.SICBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test whether block ciphers implement reset contract on init, encrypt/decrypt and reset.
+ */
+public class BlockCipherResetTest
+ extends SimpleTest
+{
+
+ public String getName()
+ {
+ return "Block Cipher Reset";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ // 128 bit block ciphers
+ testReset("AESFastEngine", new AESFastEngine(), new AESFastEngine(), new KeyParameter(new byte[16]));
+ testReset("AESEngine", new AESEngine(), new AESEngine(), new KeyParameter(new byte[16]));
+ testReset("AESLightEngine", new AESLightEngine(), new AESLightEngine(), new KeyParameter(new byte[16]));
+ testReset("Twofish", new TwofishEngine(), new TwofishEngine(), new KeyParameter(new byte[16]));
+ testReset("NoekeonEngine", new NoekeonEngine(), new NoekeonEngine(), new KeyParameter(new byte[16]));
+ testReset("SerpentEngine", new SerpentEngine(), new SerpentEngine(), new KeyParameter(new byte[16]));
+ testReset("SEEDEngine", new SEEDEngine(), new SEEDEngine(), new KeyParameter(new byte[16]));
+ testReset("CAST6Engine", new CAST6Engine(), new CAST6Engine(), new KeyParameter(new byte[16]));
+ testReset("RC6Engine", new RC6Engine(), new RC6Engine(), new KeyParameter(new byte[16]));
+
+ // 64 bit block ciphers
+ testReset("DESEngine", new DESEngine(), new DESEngine(), new KeyParameter(new byte[8]));
+ testReset("BlowfishEngine", new BlowfishEngine(), new BlowfishEngine(), new KeyParameter(new byte[8]));
+ testReset("CAST5Engine", new CAST5Engine(), new CAST5Engine(), new KeyParameter(new byte[8]));
+ testReset("DESedeEngine", new DESedeEngine(), new DESedeEngine(), new KeyParameter(new byte[24]));
+ testReset("TEAEngine", new TEAEngine(), new TEAEngine(), new KeyParameter(new byte[16]));
+ testReset("XTEAEngine", new XTEAEngine(), new XTEAEngine(), new KeyParameter(new byte[16]));
+
+ // primitive block cipher modes (don't reset on processBlock)
+ testModeReset("AES/CBC", new CBCBlockCipher(new AESEngine()), new CBCBlockCipher(new AESEngine()),
+ new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ testModeReset("AES/SIC", new SICBlockCipher(new AESEngine()), new SICBlockCipher(new AESEngine()),
+ new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ testModeReset("AES/CFB", new CFBBlockCipher(new AESEngine(), 128), new CFBBlockCipher(new AESEngine(), 128),
+ new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ testModeReset("AES/OFB", new OFBBlockCipher(new AESEngine(), 128), new OFBBlockCipher(new AESEngine(), 128),
+ new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ testModeReset("AES/GCTR", new GOFBBlockCipher(new DESEngine()), new GOFBBlockCipher(new DESEngine()),
+ new ParametersWithIV(new KeyParameter(new byte[8]), new byte[8]));
+ testModeReset("AES/OpenPGPCFB", new OpenPGPCFBBlockCipher(new AESEngine()), new OpenPGPCFBBlockCipher(
+ new AESEngine()), new KeyParameter(new byte[16]));
+ testModeReset("AES/PGPCFB", new PGPCFBBlockCipher(new AESEngine(), false), new PGPCFBBlockCipher(
+ new AESEngine(), false), new KeyParameter(new byte[16]));
+
+ // PGPCFB with IV is broken (it's also not a PRP, so probably shouldn't be a BlockCipher)
+ // testModeReset("AES/PGPCFBwithIV", new PGPCFBBlockCipher(new AESEngine(), true), new
+ // PGPCFBBlockCipher(
+ // new AESEngine(), true), new ParametersWithIV(new KeyParameter(new byte[16]), new
+ // byte[16]));
+ // testModeReset("AES/PGPCFBwithIV_NoIV", new PGPCFBBlockCipher(new AESEngine(), true), new
+ // PGPCFBBlockCipher(
+ // new AESEngine(), true), new KeyParameter(new byte[16]));
+
+ }
+
+ private void testModeReset(String test, BlockCipher cipher1, BlockCipher cipher2, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ testReset(test, false, cipher1, cipher2, params);
+ }
+
+ private void testReset(String test, BlockCipher cipher1, BlockCipher cipher2, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ testReset(test, true, cipher1, cipher2, params);
+ }
+
+ private void testReset(String test,
+ boolean testCryptReset,
+ BlockCipher cipher1,
+ BlockCipher cipher2,
+ CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ cipher1.init(true, params);
+
+ byte[] plaintext = new byte[cipher1.getBlockSize()];
+ byte[] ciphertext = new byte[(cipher1.getAlgorithmName().indexOf("PGPCFBwithIV")) > -1 ? 2 * cipher1.getBlockSize() + 2
+ : cipher1.getBlockSize()];
+
+ // Establish baseline answer
+ crypt(cipher1, true, plaintext, ciphertext);
+
+ // Test encryption resets
+ checkReset(test, testCryptReset, cipher1, params, true, plaintext, ciphertext);
+
+ // Test decryption resets with fresh instance
+ cipher2.init(false, params);
+ checkReset(test, testCryptReset, cipher2, params, false, ciphertext, plaintext);
+ }
+
+ private void checkReset(String test,
+ boolean testCryptReset,
+ BlockCipher cipher,
+ CipherParameters params,
+ boolean encrypt,
+ byte[] pretext,
+ byte[] posttext)
+ throws InvalidCipherTextException
+ {
+ // Do initial run
+ byte[] output = new byte[posttext.length];
+ crypt(cipher, encrypt, pretext, output);
+
+ // Check encrypt resets cipher
+ if (testCryptReset)
+ {
+ crypt(cipher, encrypt, pretext, output);
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test + (encrypt ? " encrypt" : " decrypt") + " did not reset cipher.");
+ }
+ }
+
+ // Check init resets data
+ cipher.processBlock(pretext, 0, output, 0);
+ cipher.init(encrypt, params);
+
+ try
+ {
+ crypt(cipher, encrypt, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test + " init did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test + " init did not reset data.", new String(Hex.encode(posttext)), new String(Hex.encode(output)));
+ }
+
+ // Check reset resets data
+ cipher.processBlock(pretext, 0, output, 0);
+ cipher.reset();
+
+ try
+ {
+ crypt(cipher, encrypt, pretext, output);
+ }
+ catch (DataLengthException e)
+ {
+ fail(test + " reset did not reset data.");
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(test + " reset did not reset data.");
+ }
+ }
+
+ private static void crypt(BlockCipher cipher1, boolean encrypt, byte[] plaintext, byte[] output)
+ throws InvalidCipherTextException
+ {
+ cipher1.processBlock(plaintext, 0, output, 0);
+ if ((cipher1.getAlgorithmName().indexOf("PGPCFBwithIV") > -1) && !encrypt)
+ {
+ // Process past IV in first block
+ cipher1.processBlock(plaintext, cipher1.getBlockSize(), output, 0);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new BlockCipherResetTest());
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherVectorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherVectorTest.java
new file mode 100644
index 00000000..36b729c6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlockCipherVectorTest.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * a basic test that takes a cipher, key parameter, and an input
+ * and output string. This test wraps the engine in a buffered block
+ * cipher with padding disabled.
+ */
+public class BlockCipherVectorTest
+ extends SimpleTest
+{
+ int id;
+ BlockCipher engine;
+ CipherParameters param;
+ byte[] input;
+ byte[] output;
+
+ public BlockCipherVectorTest(
+ int id,
+ BlockCipher engine,
+ CipherParameters param,
+ String input,
+ String output)
+ {
+ this.id = id;
+ this.engine = engine;
+ this.param = param;
+ this.input = Hex.decode(input);
+ this.output = Hex.decode(output);
+ }
+
+ public String getName()
+ {
+ return engine.getAlgorithmName() + " Vector Test " + id;
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BufferedBlockCipher cipher = new BufferedBlockCipher(engine);
+
+ cipher.init(true, param);
+
+ byte[] out = new byte[input.length];
+
+ int len1 = cipher.processBytes(input, 0, input.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (!areEqual(out, output))
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ cipher.init(false, param);
+
+ int len2 = cipher.processBytes(output, 0, output.length, out, 0);
+
+ cipher.doFinal(out, len2);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal got " + new String(Hex.encode(out)));
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/BlowfishTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlowfishTest.java
new file mode 100644
index 00000000..757788ed
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/BlowfishTest.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.BlowfishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * blowfish tester - vectors from http://www.counterpane.com/vectors.txt
+ */
+public class BlowfishTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("0000000000000000")),
+ "0000000000000000", "4EF997456198DD78"),
+ new BlockCipherVectorTest(1, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("FFFFFFFFFFFFFFFF")),
+ "FFFFFFFFFFFFFFFF", "51866FD5B85ECB8A"),
+ new BlockCipherVectorTest(2, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("3000000000000000")),
+ "1000000000000001", "7D856F9A613063F2"),
+ new BlockCipherVectorTest(3, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("1111111111111111")),
+ "1111111111111111", "2466DD878B963C9D"),
+ new BlockCipherVectorTest(4, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("0123456789ABCDEF")),
+ "1111111111111111", "61F9C3802281B096"),
+ new BlockCipherVectorTest(5, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("FEDCBA9876543210")),
+ "0123456789ABCDEF", "0ACEAB0FC6A0A28D"),
+ new BlockCipherVectorTest(6, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("7CA110454A1A6E57")),
+ "01A1D6D039776742", "59C68245EB05282B"),
+ new BlockCipherVectorTest(7, new BlowfishEngine(),
+ new KeyParameter(Hex.decode("0131D9619DC1376E")),
+ "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"),
+ };
+
+ BlowfishTest()
+ {
+ super(tests, new BlowfishEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "Blowfish";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new BlowfishTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CAST5Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CAST5Test.java
new file mode 100644
index 00000000..c651d873
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CAST5Test.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.CAST5Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * cast tester - vectors from http://www.ietf.org/rfc/rfc2144.txt
+ */
+public class CAST5Test
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new CAST5Engine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0123456789ABCDEF",
+ "238B4FE5847E44B2"),
+ new BlockCipherVectorTest(0, new CAST5Engine(),
+ new KeyParameter(Hex.decode("01234567123456782345")),
+ "0123456789ABCDEF",
+ "EB6A711A2C02271B"),
+ new BlockCipherVectorTest(0, new CAST5Engine(),
+ new KeyParameter(Hex.decode("0123456712")),
+ "0123456789ABCDEF",
+ "7Ac816d16E9B302E"),
+ };
+
+ CAST5Test()
+ {
+ super(tests, new CAST5Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "CAST5";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CAST5Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CAST6Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CAST6Test.java
new file mode 100644
index 00000000..ef035a5d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CAST6Test.java
@@ -0,0 +1,44 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.CAST6Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * cast6 tester - vectors from http://www.ietf.org/rfc/rfc2612.txt
+ */
+public class CAST6Test
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new CAST6Engine(),
+ new KeyParameter(Hex.decode("2342bb9efa38542c0af75647f29f615d")),
+ "00000000000000000000000000000000",
+ "c842a08972b43d20836c91d1b7530f6b"),
+ new BlockCipherVectorTest(0, new CAST6Engine(),
+ new KeyParameter(Hex.decode("2342bb9efa38542cbed0ac83940ac298bac77a7717942863")),
+ "00000000000000000000000000000000",
+ "1b386c0210dcadcbdd0e41aa08a7a7e8"),
+ new BlockCipherVectorTest(0, new CAST6Engine(),
+ new KeyParameter(Hex.decode("2342bb9efa38542cbed0ac83940ac2988d7c47ce264908461cc1b5137ae6b604")),
+ "00000000000000000000000000000000",
+ "4f6a2038286897b9c9870136553317fa")
+ };
+
+ CAST6Test()
+ {
+ super(tests, new CAST6Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "CAST6";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CAST6Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CCMTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CCMTest.java
new file mode 100644
index 00000000..2bbfe2a0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CCMTest.java
@@ -0,0 +1,305 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.modes.CCMBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * First four test vectors from
+ * NIST Special Publication 800-38C.
+ */
+public class CCMTest
+ extends SimpleTest
+{
+ private byte[] K1 = Hex.decode("404142434445464748494a4b4c4d4e4f");
+ private byte[] N1 = Hex.decode("10111213141516");
+ private byte[] A1 = Hex.decode("0001020304050607");
+ private byte[] P1 = Hex.decode("20212223");
+ private byte[] C1 = Hex.decode("7162015b4dac255d");
+ private byte[] T1 = Hex.decode("6084341b");
+
+ private byte[] K2 = Hex.decode("404142434445464748494a4b4c4d4e4f");
+ private byte[] N2 = Hex.decode("1011121314151617");
+ private byte[] A2 = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ private byte[] P2 = Hex.decode("202122232425262728292a2b2c2d2e2f");
+ private byte[] C2 = Hex.decode("d2a1f0e051ea5f62081a7792073d593d1fc64fbfaccd");
+ private byte[] T2 = Hex.decode("7f479ffca464");
+
+ private byte[] K3 = Hex.decode("404142434445464748494a4b4c4d4e4f");
+ private byte[] N3 = Hex.decode("101112131415161718191a1b");
+ private byte[] A3 = Hex.decode("000102030405060708090a0b0c0d0e0f10111213");
+ private byte[] P3 = Hex.decode("202122232425262728292a2b2c2d2e2f3031323334353637");
+ private byte[] C3 = Hex.decode("e3b201a9f5b71a7a9b1ceaeccd97e70b6176aad9a4428aa5484392fbc1b09951");
+ private byte[] T3 = Hex.decode("67c99240c7d51048");
+
+ private byte[] K4 = Hex.decode("404142434445464748494a4b4c4d4e4f");
+ private byte[] N4 = Hex.decode("101112131415161718191a1b1c");
+ private byte[] A4 = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+ private byte[] P4 = Hex.decode("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f");
+ private byte[] C4 = Hex.decode("69915dad1e84c6376a68c2967e4dab615ae0fd1faec44cc484828529463ccf72b4ac6bec93e8598e7f0dadbcea5b");
+ private byte[] T4 = Hex.decode("f4dd5d0ee404617225ffe34fce91");
+
+ //
+ // long data vector
+ //
+ private byte[] C5 = Hex.decode("49b17d8d3ea4e6174a48e2b65e6d8b417ac0dd3f8ee46ce4a4a2a509661cef52528c1cd9805333a5cfd482fa3f095a3c2fdd1cc47771c5e55fddd60b5c8d6d3fa5c8dd79d08b16242b6642106e7c0c28bd1064b31e6d7c9800c8397dbc3fa8071e6a38278b386c18d65d39c6ad1ef9501a5c8f68d38eb6474799f3cc898b4b9b97e87f9c95ce5c51bc9d758f17119586663a5684e0a0daf6520ec572b87473eb141d10471e4799ded9e607655402eca5176bbf792ef39dd135ac8d710da8e9e854fd3b95c681023f36b5ebe2fb213d0b62dd6e9e3cfe190b792ccb20c53423b2dca128f861a61d306910e1af418839467e466f0ec361d2539eedd99d4724f1b51c07beb40e875a87491ec8b27cd1");
+ private byte[] T5 = Hex.decode("5c768856796b627b13ec8641581b");
+
+ public void performTest()
+ throws Exception
+ {
+ CCMBlockCipher ccm = new CCMBlockCipher(new AESEngine());
+
+ checkVectors(0, ccm, K1, 32, N1, A1, P1, T1, C1);
+ checkVectors(1, ccm, K2, 48, N2, A2, P2, T2, C2);
+ checkVectors(2, ccm, K3, 64, N3, A3, P3, T3, C3);
+
+ ivParamTest(0, ccm, K1, N1);
+
+ //
+ // 4 has a reduced associated text which needs to be replicated
+ //
+ byte[] a4 = new byte[65536]; // 524288 / 8
+
+ for (int i = 0; i < a4.length; i += A4.length)
+ {
+ System.arraycopy(A4, 0, a4, i, A4.length);
+ }
+
+ checkVectors(3, ccm, K4, 112, N4, a4, P4, T4, C4);
+
+ //
+ // long data test
+ //
+ checkVectors(4, ccm, K4, 112, N4, A4, A4, T5, C5);
+
+ // decryption with output specified, non-zero offset.
+ ccm.init(false, new AEADParameters(new KeyParameter(K2), 48, N2, A2));
+
+ byte[] inBuf = new byte[C2.length + 10];
+ byte[] outBuf = new byte[ccm.getOutputSize(C2.length) + 10];
+
+ System.arraycopy(C2, 0, inBuf, 10, C2.length);
+
+ int len = ccm.processPacket(inBuf, 10, C2.length, outBuf, 10);
+ byte[] out = ccm.processPacket(C2, 0, C2.length);
+
+ if (len != out.length || !isEqual(out, outBuf, 10))
+ {
+ fail("decryption output incorrect");
+ }
+
+ // encryption with output specified, non-zero offset.
+ ccm.init(true, new AEADParameters(new KeyParameter(K2), 48, N2, A2));
+
+ int inLen = len;
+ inBuf = outBuf;
+ outBuf = new byte[ccm.getOutputSize(inLen) + 10];
+
+ len = ccm.processPacket(inBuf, 10, inLen, outBuf, 10);
+ out = ccm.processPacket(inBuf, 10, inLen);
+
+ if (len != out.length || !isEqual(out, outBuf, 10))
+ {
+ fail("encryption output incorrect");
+ }
+
+ //
+ // exception tests
+ //
+
+ try
+ {
+ ccm.init(false, new AEADParameters(new KeyParameter(K1), 32, N2, A2));
+
+ ccm.processPacket(C2, 0, C2.length);
+
+ fail("invalid cipher text not picked up");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ccm = new CCMBlockCipher(new DESEngine());
+
+ fail("incorrect block size not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ccm.init(false, new KeyParameter(K1));
+
+ fail("illegal argument not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ AEADTestUtil.testReset(this, new CCMBlockCipher(new AESEngine()), new CCMBlockCipher(new AESEngine()), new AEADParameters(new KeyParameter(K1), 32, N2));
+ AEADTestUtil.testTampering(this, ccm, new AEADParameters(new KeyParameter(K1), 32, N2));
+ AEADTestUtil.testOutputSizes(this, new CCMBlockCipher(new AESEngine()), new AEADParameters(
+ new KeyParameter(K1), 32, N2));
+ AEADTestUtil.testBufferSizeChecks(this, new CCMBlockCipher(new AESEngine()), new AEADParameters(
+ new KeyParameter(K1), 32, N2));
+ }
+
+ private boolean isEqual(byte[] exp, byte[] other, int off)
+ {
+ for (int i = 0; i != exp.length; i++)
+ {
+ if (exp[i] != other[off + i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void checkVectors(
+ int count,
+ CCMBlockCipher ccm,
+ byte[] k,
+ int macSize,
+ byte[] n,
+ byte[] a,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ byte[] fa = new byte[a.length / 2];
+ byte[] la = new byte[a.length - (a.length / 2)];
+ System.arraycopy(a, 0, fa, 0, fa.length);
+ System.arraycopy(a, fa.length, la, 0, la.length);
+
+ checkVectors(count, ccm, "all initial associated data", k, macSize, n, a, null, p, t, c);
+ checkVectors(count, ccm, "subsequent associated data", k, macSize, n, null, a, p, t, c);
+ checkVectors(count, ccm, "split associated data", k, macSize, n, fa, la, p, t, c);
+ checkVectors(count, ccm, "reuse key", null, macSize, n, fa, la, p, t, c);
+ }
+
+ private void checkVectors(
+ int count,
+ CCMBlockCipher ccm,
+ String additionalDataType,
+ byte[] k,
+ int macSize,
+ byte[] n,
+ byte[] a,
+ byte[] sa,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ KeyParameter keyParam = (k == null) ? null : new KeyParameter(k);
+
+ ccm.init(true, new AEADParameters(keyParam, macSize, n, a));
+
+ byte[] enc = new byte[c.length];
+
+ if (sa != null)
+ {
+ ccm.processAADBytes(sa, 0, sa.length);
+ }
+
+ int len = ccm.processBytes(p, 0, p.length, enc, 0);
+
+ len += ccm.doFinal(enc, len);
+
+ if (!areEqual(c, enc))
+ {
+ fail("encrypted stream fails to match in test " + count + " with " + additionalDataType);
+ }
+
+ ccm.init(false, new AEADParameters(keyParam, macSize, n, a));
+
+ byte[] tmp = new byte[enc.length];
+
+ if (sa != null)
+ {
+ ccm.processAADBytes(sa, 0, sa.length);
+ }
+
+ len = ccm.processBytes(enc, 0, enc.length, tmp, 0);
+
+ len += ccm.doFinal(tmp, len);
+
+ byte[] dec = new byte[len];
+
+ System.arraycopy(tmp, 0, dec, 0, len);
+
+ if (!areEqual(p, dec))
+ {
+ fail("decrypted stream fails to match in test " + count + " with " + additionalDataType,
+ new String(Hex.encode(p)), new String(Hex.encode(dec)));
+ }
+
+ if (!areEqual(t, ccm.getMac()))
+ {
+ fail("MAC fails to match in test " + count + " with " + additionalDataType);
+ }
+ }
+
+ private void ivParamTest(
+ int count,
+ CCMBlockCipher ccm,
+ byte[] k,
+ byte[] n)
+ throws InvalidCipherTextException
+ {
+ byte[] p = Strings.toByteArray("hello world!!");
+
+ ccm.init(true, new ParametersWithIV(new KeyParameter(k), n));
+
+ byte[] enc = new byte[p.length + 8];
+
+ int len = ccm.processBytes(p, 0, p.length, enc, 0);
+
+ len += ccm.doFinal(enc, len);
+
+ ccm.init(false, new ParametersWithIV(new KeyParameter(k), n));
+
+ byte[] tmp = new byte[enc.length];
+
+ len = ccm.processBytes(enc, 0, enc.length, tmp, 0);
+
+ len += ccm.doFinal(tmp, len);
+
+ byte[] dec = new byte[len];
+
+ System.arraycopy(tmp, 0, dec, 0, len);
+
+ if (!areEqual(p, dec))
+ {
+ fail("decrypted stream fails to match in test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "CCM";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CCMTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CMacTest.java
new file mode 100644
index 00000000..720f8ea3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CMacTest.java
@@ -0,0 +1,321 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.macs.CMac;
+import org.bouncycastle.crypto.macs.CMacWithIV;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * CMAC tester - <a href="http://www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/tv/omac1-tv.txt">Official Test Vectors</a>.
+ */
+public class CMacTest
+ extends SimpleTest
+{
+ private static final byte[] keyBytes128 = Hex.decode("2b7e151628aed2a6abf7158809cf4f3c");
+ private static final byte[] keyBytes192 = Hex.decode(
+ "8e73b0f7da0e6452c810f32b809079e5"
+ + "62f8ead2522c6b7b");
+ private static final byte[] keyBytes256 = Hex.decode(
+ "603deb1015ca71be2b73aef0857d7781"
+ + "1f352c073b6108d72d9810a30914dff4");
+
+ private static final byte[] input0 = Hex.decode("");
+ private static final byte[] input16 = Hex.decode("6bc1bee22e409f96e93d7e117393172a");
+ private static final byte[] input40 = Hex.decode(
+ "6bc1bee22e409f96e93d7e117393172a"
+ + "ae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411");
+ private static final byte[] input64 = Hex.decode(
+ "6bc1bee22e409f96e93d7e117393172a"
+ + "ae2d8a571e03ac9c9eb76fac45af8e51"
+ + "30c81c46a35ce411e5fbc1191a0a52ef"
+ + "f69f2445df4f9b17ad2b417be66c3710");
+
+ private static final byte[] output_k128_m0 = Hex.decode("bb1d6929e95937287fa37d129b756746");
+ private static final byte[] output_k128_m16 = Hex.decode("070a16b46b4d4144f79bdd9dd04a287c");
+ private static final byte[] output_k128_m40 = Hex.decode("dfa66747de9ae63030ca32611497c827");
+ private static final byte[] output_k128_m64 = Hex.decode("51f0bebf7e3b9d92fc49741779363cfe");
+
+ private static final byte[] output_k192_m0 = Hex.decode("d17ddf46adaacde531cac483de7a9367");
+ private static final byte[] output_k192_m16 = Hex.decode("9e99a7bf31e710900662f65e617c5184");
+ private static final byte[] output_k192_m40 = Hex.decode("8a1de5be2eb31aad089a82e6ee908b0e");
+ private static final byte[] output_k192_m64 = Hex.decode("a1d5df0eed790f794d77589659f39a11");
+
+ private static final byte[] output_k256_m0 = Hex.decode("028962f61b7bf89efc6b551f4667d983");
+ private static final byte[] output_k256_m16 = Hex.decode("28a7023f452e8f82bd4bf28d8c37c35c");
+ private static final byte[] output_k256_m40 = Hex.decode("aaf3d8f1de5640c232f5b169b9c911e6");
+ private static final byte[] output_k256_m64 = Hex.decode("e1992190549f6ed5696a2c056c315410");
+
+ public CMacTest()
+ {
+ }
+
+ public void performTest()
+ {
+ BlockCipher cipher = new AESFastEngine();
+ Mac mac = new CMac(cipher, 128);
+
+ //128 bytes key
+
+ KeyParameter key = new KeyParameter(keyBytes128);
+
+ // 0 bytes message - 128 bytes key
+ mac.init(key);
+
+ mac.update(input0, 0, input0.length);
+
+ byte[] out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k128_m0))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k128_m0))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 16 bytes message - 128 bytes key
+ mac.init(key);
+
+ mac.update(input16, 0, input16.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k128_m16))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k128_m16))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 40 bytes message - 128 bytes key
+ mac.init(key);
+
+ mac.update(input40, 0, input40.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k128_m40))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k128_m40))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 64 bytes message - 128 bytes key
+ mac.init(key);
+
+ mac.update(input64, 0, input64.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k128_m64))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k128_m64))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ //192 bytes key
+
+ key = new KeyParameter(keyBytes192);
+
+ // 0 bytes message - 192 bytes key
+ mac.init(key);
+
+ mac.update(input0, 0, input0.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k192_m0))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k192_m0))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 16 bytes message - 192 bytes key
+ mac.init(key);
+
+ mac.update(input16, 0, input16.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k192_m16))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k192_m16))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 40 bytes message - 192 bytes key
+ mac.init(key);
+
+ mac.update(input40, 0, input40.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k192_m40))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k192_m40))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 64 bytes message - 192 bytes key
+ mac.init(key);
+
+ mac.update(input64, 0, input64.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k192_m64))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k192_m64))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ //256 bytes key
+
+ key = new KeyParameter(keyBytes256);
+
+ // 0 bytes message - 256 bytes key
+ mac.init(key);
+
+ mac.update(input0, 0, input0.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m0))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m0))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 16 bytes message - 256 bytes key
+ mac.init(key);
+
+ mac.update(input16, 0, input16.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m16))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m16))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 40 bytes message - 256 bytes key
+ mac.init(key);
+
+ mac.update(input40, 0, input40.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m40))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m40))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // 64 bytes message - 256 bytes key
+ mac.init(key);
+
+ mac.update(input64, 0, input64.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m64))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m64))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // CMAC with IV
+ // 16 bytes message - 256 bytes key
+ mac = new CMacWithIV(new AESFastEngine());
+
+ mac.init(key);
+
+ mac.update(input16, 0, input16.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output_k256_m16))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output_k256_m16))
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ // CMAC with IV
+ // 16 bytes message - 256 bytes key
+ mac = new CMacWithIV(new AESFastEngine());
+
+ mac.init(new ParametersWithIV(key, Hex.decode("000102030405060708090a0b0c0d0e0f")));
+
+ mac.update(input16, 0, input16.length);
+
+ out = new byte[16];
+
+ mac.doFinal(out, 0);
+
+ if (areEqual(out, output_k256_m16))
+ {
+ fail("Failed - got " + new String(Hex.encode(output_k256_m16)));
+ }
+
+ if (!areEqual(out, Hex.decode("9347a60c64061b9ff2a92522ca8e08fc")))
+ {
+ fail("Failed - expected " + "9347a60c64061b9ff2a92522ca8e08fc"
+ + " got " + new String(Hex.encode(out)));
+ }
+
+ testExceptions();
+ }
+
+ private void testExceptions()
+ {
+ try
+ {
+ CMac mac = new CMac(new AESEngine());
+ mac.init(new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ fail("CMac does not accept IV");
+ } catch(IllegalArgumentException e)
+ {
+ // Expected
+ }
+ }
+
+ public String getName()
+ {
+ return "CMac";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new CMacTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CTSTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CTSTest.java
new file mode 100644
index 00000000..28fcc201
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CTSTest.java
@@ -0,0 +1,212 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.engines.SkipjackEngine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CTSBlockCipher;
+import org.bouncycastle.crypto.modes.OldCTSBlockCipher;
+import org.bouncycastle.crypto.modes.SICBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * CTS tester
+ */
+public class CTSTest
+ extends SimpleTest
+{
+ static byte[] in1 = Hex.decode("4e6f7720697320746865207420");
+ static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa");
+ static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98");
+ static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994");
+ static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6");
+
+ private void testCTS(
+ int id,
+ BlockCipher cipher,
+ CipherParameters params,
+ byte[] input,
+ byte[] output)
+ throws Exception
+ {
+ byte[] out = new byte[input.length];
+ BufferedBlockCipher engine = new CTSBlockCipher(cipher);
+
+ engine.init(true, params);
+
+ int len = engine.processBytes(input, 0, input.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(output, out))
+ {
+ fail("failed encryption expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ engine.init(false, params);
+
+ len = engine.processBytes(output, 0, output.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed decryption expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ private void testOldCTS(
+ int id,
+ BlockCipher cipher,
+ CipherParameters params,
+ byte[] input,
+ byte[] output)
+ throws Exception
+ {
+ byte[] out = new byte[input.length];
+ BufferedBlockCipher engine = new OldCTSBlockCipher(cipher);
+
+ engine.init(true, params);
+
+ int len = engine.processBytes(input, 0, input.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(output, out))
+ {
+ fail("failed encryption expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ engine.init(false, params);
+
+ len = engine.processBytes(output, 0, output.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed decryption expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ private void testExceptions() throws InvalidCipherTextException
+ {
+ BufferedBlockCipher engine = new CTSBlockCipher(new DESEngine());
+ CipherParameters params = new KeyParameter(new byte[engine.getBlockSize()]);
+ engine.init(true, params);
+
+ byte[] out = new byte[engine.getOutputSize(engine.getBlockSize())];
+
+ engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ fail("Expected CTS encrypt error on < 1 block input");
+ } catch(DataLengthException e)
+ {
+ // Expected
+ }
+
+ engine.init(true, params);
+ engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ } catch(DataLengthException e)
+ {
+ fail("Unexpected CTS encrypt error on == 1 block input");
+ }
+
+ engine.init(false, params);
+ engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ fail("Expected CTS decrypt error on < 1 block input");
+ } catch(DataLengthException e)
+ {
+ // Expected
+ }
+
+ engine.init(false, params);
+ engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ } catch(DataLengthException e)
+ {
+ fail("Unexpected CTS decrypt error on == 1 block input");
+ }
+
+ try
+ {
+ new CTSBlockCipher(new SICBlockCipher(new AESEngine()));
+ fail("Expected CTS construction error - only ECB/CBC supported.");
+ } catch(IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ }
+
+ public String getName()
+ {
+ return "CTS";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ byte[] key1 = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF };
+ byte[] key2 = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF, (byte)0xee, (byte)0xff };
+ byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ testCTS(1, new DESEngine(), new KeyParameter(key1), in1, out1);
+ testCTS(2, new CBCBlockCipher(new DESEngine()), new ParametersWithIV(new KeyParameter(key1), iv), in1, out2);
+ testCTS(3, new CBCBlockCipher(new SkipjackEngine()), new ParametersWithIV(new KeyParameter(key2), iv), in2, out3);
+
+ //
+ // test vectors from rfc3962
+ //
+ byte[] aes128 = Hex.decode("636869636b656e207465726979616b69");
+ byte[] aesIn1 = Hex.decode("4920776f756c64206c696b652074686520");
+ byte[] aesOut1 = Hex.decode("c6353568f2bf8cb4d8a580362da7ff7f97");
+ byte[] aesIn2 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c20476175277320");
+ byte[] aesOut2 = Hex.decode("fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5");
+ byte[] aesIn3 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c2047617527732043");
+ byte[] aesOut3 = Hex.decode("39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584");
+
+ testCTS(4, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn1, aesOut1);
+ testCTS(5, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn2, aesOut2);
+ testCTS(6, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn3, aesOut3);
+
+ testOldCTS(4, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn1, aesOut1);
+ testOldCTS(5, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn2, aesOut2);
+ testOldCTS(6, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn3, aesOut3);
+
+ byte[] aes1Block = Hex.decode("4920776f756c64206c696b6520746865");
+ byte[] preErrata = Hex.decode("e7664c13ff28c965b0d2a0e7ec353706"); // CTS style one block
+ byte[] pstErrata = Hex.decode("97687268d6ecccc0c07b25e25ecfe584"); // CBC style one block
+ byte[] pstErrataNonZeroIV = Hex.decode("571f5108c53fe95ab52df783df933fa3");
+
+ testCTS(7, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aes1Block, pstErrata);
+ testCTS(8, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), aes1Block), aes1Block, pstErrataNonZeroIV);
+ testOldCTS(7, new CBCBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aes1Block, preErrata);
+
+ testExceptions();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CTSTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CamelliaLightTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CamelliaLightTest.java
new file mode 100644
index 00000000..6198ce66
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CamelliaLightTest.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.CamelliaLightEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Camellia tester - vectors from https://www.cosic.esat.kuleuven.be/nessie/testvectors/ and RFC 3713
+ */
+public class CamelliaLightTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000", "07923A39EB0A817D1C4D87BDB82D1F1C"),
+ new BlockCipherVectorTest(1, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "6C227F749319A3AA7DA235A9BBA05A2C"),
+ new BlockCipherVectorTest(2, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba9876543210")),
+ "0123456789abcdeffedcba9876543210", "67673138549669730857065648eabe43"),
+ //
+ // 192 bit
+ //
+ new BlockCipherVectorTest(3, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba98765432100011223344556677")),
+ "0123456789abcdeffedcba9876543210", "b4993401b3e996f84ee5cee7d79b09b9"),
+ new BlockCipherVectorTest(4, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00040000000000000000000000000000", "9BCA6C88B928C1B0F57F99866583A9BC"),
+ new BlockCipherVectorTest(5, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("949494949494949494949494949494949494949494949494")),
+ "636EB22D84B006381235641BCF0308D2", "94949494949494949494949494949494"),
+ //
+ // 256 bit
+ //
+ new BlockCipherVectorTest(6, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff")),
+ "0123456789abcdeffedcba9876543210", "9acc237dff16d76c20ef7c919e3a7509"),
+ new BlockCipherVectorTest(7, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A")),
+ "057764FE3A500EDBD988C5C3B56CBA9A", "4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A"),
+ new BlockCipherVectorTest(8, new CamelliaLightEngine(),
+ new KeyParameter(Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")),
+ "7968B08ABA92193F2295121EF8D75C8A", "03030303030303030303030303030303"),
+ };
+
+ CamelliaLightTest()
+ {
+ super(tests, new CamelliaLightEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "CamelliaLight";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new CamelliaLightTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CamelliaTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CamelliaTest.java
new file mode 100644
index 00000000..e69a82ad
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CamelliaTest.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.CamelliaEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * Camellia tester - vectors from https://www.cosic.esat.kuleuven.be/nessie/testvectors/ and RFC 3713
+ */
+public class CamelliaTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000", "07923A39EB0A817D1C4D87BDB82D1F1C"),
+ new BlockCipherVectorTest(1, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "6C227F749319A3AA7DA235A9BBA05A2C"),
+ new BlockCipherVectorTest(2, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba9876543210")),
+ "0123456789abcdeffedcba9876543210", "67673138549669730857065648eabe43"),
+ //
+ // 192 bit
+ //
+ new BlockCipherVectorTest(3, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba98765432100011223344556677")),
+ "0123456789abcdeffedcba9876543210", "b4993401b3e996f84ee5cee7d79b09b9"),
+ new BlockCipherVectorTest(4, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00040000000000000000000000000000", "9BCA6C88B928C1B0F57F99866583A9BC"),
+ new BlockCipherVectorTest(5, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("949494949494949494949494949494949494949494949494")),
+ "636EB22D84B006381235641BCF0308D2", "94949494949494949494949494949494"),
+ //
+ // 256 bit
+ //
+ new BlockCipherVectorTest(6, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff")),
+ "0123456789abcdeffedcba9876543210", "9acc237dff16d76c20ef7c919e3a7509"),
+ new BlockCipherVectorTest(7, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A")),
+ "057764FE3A500EDBD988C5C3B56CBA9A", "4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A"),
+ new BlockCipherVectorTest(8, new CamelliaEngine(),
+ new KeyParameter(Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")),
+ "7968B08ABA92193F2295121EF8D75C8A", "03030303030303030303030303030303"),
+ };
+
+ CamelliaTest()
+ {
+ super(tests, new CamelliaEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Camellia";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ CamelliaTest test = new CamelliaTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ChaChaTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ChaChaTest.java
new file mode 100644
index 00000000..45c063fe
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ChaChaTest.java
@@ -0,0 +1,403 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.ChaChaEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * ChaCha Test
+ * <p>
+ * Test cases generated using ref version of ChaCha20 in estreambench-20080905.
+ */
+public class ChaChaTest
+ extends SimpleTest
+{
+ byte[] zeroes = Hex.decode(
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000");
+
+ String set1v0_0 = "FBB87FBB8395E05DAA3B1D683C422046"
+ + "F913985C2AD9B23CFC06C1D8D04FF213"
+ + "D44A7A7CDB84929F915420A8A3DC58BF"
+ + "0F7ECB4B1F167BB1A5E6153FDAF4493D";
+
+ String set1v0_192 = "D9485D55B8B82D792ED1EEA8E93E9BC1"
+ + "E2834AD0D9B11F3477F6E106A2F6A5F2"
+ + "EA8244D5B925B8050EAB038F58D4DF57"
+ + "7FAFD1B89359DAE508B2B10CBD6B488E";
+
+ String set1v0_256 = "08661A35D6F02D3D9ACA8087F421F7C8"
+ + "A42579047D6955D937925BA21396DDD4"
+ + "74B1FC4ACCDCAA33025B4BCE817A4FBF"
+ + "3E5D07D151D7E6FE04934ED466BA4779";
+
+ String set1v0_448 = "A7E16DD38BA48CCB130E5BE9740CE359"
+ + "D631E91600F85C8A5D0785A612D1D987"
+ + "90780ACDDC26B69AB106CCF6D866411D"
+ + "10637483DBF08CC5591FD8B3C87A3AE0";
+
+ String set1v9_0 = "A276339F99316A913885A0A4BE870F06"
+ + "91E72B00F1B3F2239F714FE81E88E00C"
+ + "BBE52B4EBBE1EA15894E29658C4CB145"
+ + "E6F89EE4ABB045A78514482CE75AFB7C";
+
+ String set1v9_192 = "0DFB9BD4F87F68DE54FBC1C6428FDEB0"
+ + "63E997BE8490C9B7A4694025D6EBA2B1"
+ + "5FE429DB82A7CAE6AAB22918E8D00449"
+ + "6FB6291467B5AE81D4E85E81D8795EBB";
+
+ String set1v9_256 = "546F5BB315E7F71A46E56D4580F90889"
+ + "639A2BA528F757CF3B048738BA141AF3"
+ + "B31607CB21561BAD94721048930364F4"
+ + "B1227CFEB7CDECBA881FB44903550E68";
+
+ String set1v9_448 = "6F813586E76691305A0CF048C0D8586D"
+ + "C89460207D8B230CD172398AA33D19E9"
+ + "2D24883C3A9B0BB7CD8C6B2668DB142E"
+ + "37A97948A7A01498A21110297984CD20";
+
+ String set6v0_0 = "57459975BC46799394788DE80B928387"
+ + "862985A269B9E8E77801DE9D874B3F51"
+ + "AC4610B9F9BEE8CF8CACD8B5AD0BF17D"
+ + "3DDF23FD7424887EB3F81405BD498CC3";
+
+ String set6v0_65472 = "EF9AEC58ACE7DB427DF012B2B91A0C1E"
+ + "8E4759DCE9CDB00A2BD59207357BA06C"
+ + "E02D327C7719E83D6348A6104B081DB0"
+ + "3908E5186986AE41E3AE95298BB7B713";
+
+ String set6v0_65536 = "17EF5FF454D85ABBBA280F3A94F1D26E"
+ + "950C7D5B05C4BB3A78326E0DC5731F83"
+ + "84205C32DB867D1B476CE121A0D7074B"
+ + "AA7EE90525D15300F48EC0A6624BD0AF";
+
+ String set6v1_0 = "92A2508E2C4084567195F2A1005E552B"
+ + "4874EC0504A9CD5E4DAF739AB553D2E7"
+ + "83D79C5BA11E0653BEBB5C116651302E"
+ + "8D381CB728CA627B0B246E83942A2B99";
+
+ String set6v1_65472 = "E1974EC3063F7BD0CBA58B1CE34BC874"
+ + "67AAF5759B05EA46682A5D4306E5A76B"
+ + "D99A448DB8DE73AF97A73F5FBAE2C776"
+ + "35040464524CF14D7F08D4CE1220FD84";
+
+ String set6v1_65536 = "BE3436141CFD62D12FF7D852F80C1344"
+ + "81F152AD0235ECF8CA172C55CA8C031B"
+ + "2E785D773A988CA8D4BDA6FAE0E493AA"
+ + "71DCCC4C894D1F106CAC62A9FC0A9607";
+
+ // ChaCha12
+ String chacha12_set1v0_0 = "36CF0D56E9F7FBF287BC5460D95FBA94"
+ + "AA6CBF17D74E7C784DDCF7E0E882DDAE"
+ + "3B5A58243EF32B79A04575A8E2C2B73D"
+ + "C64A52AA15B9F88305A8F0CA0B5A1A25";
+
+ String chacha12_set1v0_192 = "83496792AB68FEC75ADB16D3044420A4"
+ + "A00A6E9ADC41C3A63DBBF317A8258C85"
+ + "A9BC08B4F76B413A4837324AEDF8BC2A"
+ + "67D53C9AB9E1C5BC5F379D48DF9AF730";
+
+ String chacha12_set1v0_256 = "BAA28ED593690FD760ADA07C95E3B888"
+ + "4B4B64E488CA7A2D9BDC262243AB9251"
+ + "394C5037E255F8BCCDCD31306C508FFB"
+ + "C9E0161380F7911FCB137D46D9269250";
+
+ String chacha12_set1v0_448 = "B7ECFB6AE0B51915762FE1FD03A14D0C"
+ + "9E54DA5DC76EB16EBA5313BC535DE63D"
+ + "C72D7F9F1874E301E99C8531819F4E37"
+ + "75793F6A5D19C717FA5C78A39EB804A6";
+
+ // ChaCha8
+ String chacha8_set1v0_0 = "BEB1E81E0F747E43EE51922B3E87FB38"
+ + "D0163907B4ED49336032AB78B67C2457"
+ + "9FE28F751BD3703E51D876C017FAA435"
+ + "89E63593E03355A7D57B2366F30047C5";
+
+ String chacha8_set1v0_192 = "33B8B7CA8F8E89F0095ACE75A379C651"
+ + "FD6BDD55703C90672E44C6BAB6AACDD8"
+ + "7C976A87FD264B906E749429284134C2"
+ + "38E3B88CF74A68245B860D119A8BDF43";
+
+ String chacha8_set1v0_256 = "F7CA95BF08688BD3BE8A27724210F9DC"
+ + "16F32AF974FBFB09E9F757C577A245AB"
+ + "F35F824B70A4C02CB4A8D7191FA8A5AD"
+ + "6A84568743844703D353B7F00A8601F4";
+
+ String chacha8_set1v0_448 = "7B4117E8BFFD595CD8482270B08920FB"
+ + "C9B97794E1809E07BB271BF07C861003"
+ + "4C38DBA6ECA04E5474F399A284CBF6E2"
+ + "7F70142E604D0977797DE5B58B6B25E0";
+
+
+
+ public String getName()
+ {
+ return "ChaCha";
+ }
+
+ public void performTest()
+ {
+ chachaTest1(20, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ set1v0_0, set1v0_192, set1v0_256, set1v0_448);
+ chachaTest1(20, new ParametersWithIV(new KeyParameter(Hex.decode("00400000000000000000000000000000")), Hex.decode("0000000000000000")),
+ set1v9_0, set1v9_192, set1v9_256, set1v9_448);
+ chachaTest1(12, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ chacha12_set1v0_0, chacha12_set1v0_192, chacha12_set1v0_256, chacha12_set1v0_448);
+ chachaTest1(8, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ chacha8_set1v0_0, chacha8_set1v0_192, chacha8_set1v0_256, chacha8_set1v0_448);
+ chachaTest2(new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE")),
+ set6v0_0, set6v0_65472, set6v0_65536);
+ chachaTest2(new ParametersWithIV(new KeyParameter(Hex.decode("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12")), Hex.decode("167DE44BB21980E7")),
+ set6v1_0, set6v1_65472, set6v1_65536);
+ reinitBug();
+ skipTest();
+ }
+
+ private void chachaTest1(int rounds, CipherParameters params, String v0, String v192, String v256, String v448)
+ {
+ StreamCipher chaCha = new ChaChaEngine(rounds);
+ byte[] buf = new byte[64];
+
+ chaCha.init(true, params);
+
+ for (int i = 0; i != 7; i++)
+ {
+ chaCha.processBytes(zeroes, 0, 64, buf, 0);
+ switch (i)
+ {
+ case 0:
+ if (!areEqual(buf, Hex.decode(v0)))
+ {
+ mismatch("v0/" + rounds, v0, buf);
+ }
+ break;
+ case 3:
+ if (!areEqual(buf, Hex.decode(v192)))
+ {
+ mismatch("v192/" + rounds, v192, buf);
+ }
+ break;
+ case 4:
+ if (!areEqual(buf, Hex.decode(v256)))
+ {
+ mismatch("v256/" + rounds, v256, buf);
+ }
+ break;
+ default:
+ // ignore
+ }
+ }
+
+ for (int i = 0; i != 64; i++)
+ {
+ buf[i] = chaCha.returnByte(zeroes[i]);
+ }
+
+ if (!areEqual(buf, Hex.decode(v448)))
+ {
+ mismatch("v448", v448, buf);
+ }
+ }
+
+ private void chachaTest2(CipherParameters params, String v0, String v65472, String v65536)
+ {
+ StreamCipher chaCha = new ChaChaEngine();
+ byte[] buf = new byte[64];
+
+ chaCha.init(true, params);
+
+ for (int i = 0; i != 1025; i++)
+ {
+ chaCha.processBytes(zeroes, 0, 64, buf, 0);
+ switch (i)
+ {
+ case 0:
+ if (!areEqual(buf, Hex.decode(v0)))
+ {
+ mismatch("v0", v0, buf);
+ }
+ break;
+ case 1023:
+ if (!areEqual(buf, Hex.decode(v65472)))
+ {
+ mismatch("v65472", v65472, buf);
+ }
+ break;
+ case 1024:
+ if (!areEqual(buf, Hex.decode(v65536)))
+ {
+ mismatch("v65536", v65536, buf);
+ }
+ break;
+ default:
+ // ignore
+ }
+ }
+ }
+
+ private void mismatch(String name, String expected, byte[] found)
+ {
+ fail("mismatch on " + name, expected, new String(Hex.encode(found)));
+ }
+
+
+ private void reinitBug()
+ {
+ KeyParameter key = new KeyParameter(Hex.decode("80000000000000000000000000000000"));
+ ParametersWithIV parameters = new ParametersWithIV(key, Hex.decode("0000000000000000"));
+
+ StreamCipher salsa = new ChaChaEngine();
+
+ salsa.init(true, parameters);
+
+ try
+ {
+ salsa.init(true, key);
+ fail("Salsa20 should throw exception if no IV in Init");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ }
+
+ private boolean areEqual(byte[] a, int aOff, byte[] b, int bOff)
+ {
+ for (int i = bOff; i != b.length; i++)
+ {
+ if (a[aOff + i - bOff] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void skipTest()
+ {
+ SecureRandom rand = new SecureRandom();
+ byte[] plain = new byte[5000];
+ byte[] cipher = new byte[5000];
+
+ rand.nextBytes(plain);
+
+ CipherParameters params = new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE"));
+ ChaChaEngine engine = new ChaChaEngine();
+
+ engine.init(true, params);
+
+ engine.processBytes(plain, 0, plain.length, cipher, 0);
+
+ byte[] fragment = new byte[20];
+
+ engine.init(true, params);
+
+ engine.skip(10);
+
+ engine.processBytes(plain, 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 10, fragment, 0))
+ {
+ fail("skip forward 10 failed");
+ }
+
+ engine.skip(1000);
+
+ engine.processBytes(plain, 1010 + fragment.length, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + fragment.length, fragment, 0))
+ {
+ fail("skip forward 1000 failed");
+ }
+
+ engine.skip(-10);
+
+ engine.processBytes(plain, 1010 + 2 * fragment.length - 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + 2 * fragment.length - 10, fragment, 0))
+ {
+ fail("skip back 10 failed");
+ }
+
+ engine.skip(-1000);
+
+ if (engine.getPosition() != 60)
+ {
+ fail("skip position incorrect - " + 60 + " got " + engine.getPosition());
+ }
+
+ engine.processBytes(plain, 60, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 60, fragment, 0))
+ {
+ fail("skip back 1000 failed");
+ }
+
+ long pos = engine.seekTo(1010);
+ if (pos != 1010)
+ {
+ fail("position wrong");
+ }
+
+ engine.processBytes(plain, 1010, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010, fragment, 0))
+ {
+ fail("seek to 1010 failed");
+ }
+
+ engine.reset();
+
+ for (int i = 0; i != 1000; i++)
+ {
+ engine.skip(i);
+
+ if (engine.getPosition() != i)
+ {
+ fail("skip forward at wrong position");
+ }
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip forward i failed: " + i);
+ }
+
+ if (engine.getPosition() != i + fragment.length)
+ {
+ fail("cipher at wrong position: " + engine.getPosition() + " [" + i + "]");
+ }
+
+ engine.skip(-fragment.length);
+
+ if (engine.getPosition() != i)
+ {
+ fail("skip back at wrong position");
+ }
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip back i failed: " + i);
+ }
+
+ engine.reset();
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ChaChaTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CipherStreamTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CipherStreamTest.java
new file mode 100644
index 00000000..089e4ed8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CipherStreamTest.java
@@ -0,0 +1,706 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.BlowfishEngine;
+import org.bouncycastle.crypto.engines.CAST5Engine;
+import org.bouncycastle.crypto.engines.CAST6Engine;
+import org.bouncycastle.crypto.engines.CamelliaEngine;
+import org.bouncycastle.crypto.engines.ChaChaEngine;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.Grain128Engine;
+import org.bouncycastle.crypto.engines.Grainv1Engine;
+import org.bouncycastle.crypto.engines.HC128Engine;
+import org.bouncycastle.crypto.engines.HC256Engine;
+import org.bouncycastle.crypto.engines.NoekeonEngine;
+import org.bouncycastle.crypto.engines.RC2Engine;
+import org.bouncycastle.crypto.engines.RC4Engine;
+import org.bouncycastle.crypto.engines.RC6Engine;
+import org.bouncycastle.crypto.engines.SEEDEngine;
+import org.bouncycastle.crypto.engines.Salsa20Engine;
+import org.bouncycastle.crypto.engines.SerpentEngine;
+import org.bouncycastle.crypto.engines.TEAEngine;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.engines.XSalsa20Engine;
+import org.bouncycastle.crypto.engines.XTEAEngine;
+import org.bouncycastle.crypto.io.CipherInputStream;
+import org.bouncycastle.crypto.io.CipherOutputStream;
+import org.bouncycastle.crypto.io.InvalidCipherTextIOException;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CCMBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.CTSBlockCipher;
+import org.bouncycastle.crypto.modes.EAXBlockCipher;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.modes.NISTCTSBlockCipher;
+import org.bouncycastle.crypto.modes.OCBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.crypto.modes.SICBlockCipher;
+import org.bouncycastle.crypto.paddings.PKCS7Padding;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class CipherStreamTest
+ extends SimpleTest
+{
+ private int streamSize;
+
+ public String getName()
+ {
+ return "CipherStreamTest";
+ }
+
+ private void testMode(Object cipher, CipherParameters params)
+ throws Exception
+ {
+ testWriteRead(cipher, params, false);
+ testWriteRead(cipher, params, true);
+ testReadWrite(cipher, params, false);
+ testReadWrite(cipher, params, true);
+
+ if (!(cipher instanceof CTSBlockCipher || cipher instanceof NISTCTSBlockCipher))
+ {
+ testWriteReadEmpty(cipher, params, false);
+ testWriteReadEmpty(cipher, params, true);
+ }
+
+ if (cipher instanceof AEADBlockCipher)
+ {
+ testTamperedRead((AEADBlockCipher)cipher, params);
+ testTruncatedRead((AEADBlockCipher)cipher, params);
+ testTamperedWrite((AEADBlockCipher)cipher, params);
+ }
+ }
+
+ private OutputStream createCipherOutputStream(OutputStream output, Object cipher)
+ {
+ if (cipher instanceof BufferedBlockCipher)
+ {
+ return new CipherOutputStream(output, (BufferedBlockCipher)cipher);
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ return new CipherOutputStream(output, (AEADBlockCipher)cipher);
+ }
+ else
+ {
+ return new CipherOutputStream(output, (StreamCipher)cipher);
+ }
+ }
+
+ private InputStream createCipherInputStream(byte[] data, Object cipher)
+ {
+ ByteArrayInputStream input = new ByteArrayInputStream(data);
+ if (cipher instanceof BufferedBlockCipher)
+ {
+ return new CipherInputStream(input, (BufferedBlockCipher)cipher);
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ return new CipherInputStream(input, (AEADBlockCipher)cipher);
+ }
+ else
+ {
+ return new CipherInputStream(input, (StreamCipher)cipher);
+ }
+ }
+
+ /**
+ * Test tampering of ciphertext followed by read from decrypting CipherInputStream
+ */
+ private void testTamperedRead(AEADBlockCipher cipher, CipherParameters params)
+ throws Exception
+ {
+ cipher.init(true, params);
+
+ byte[] ciphertext = new byte[cipher.getOutputSize(streamSize)];
+ cipher.doFinal(ciphertext, cipher.processBytes(new byte[streamSize], 0, streamSize, ciphertext, 0));
+
+ // Tamper
+ ciphertext[0] += 1;
+
+ cipher.init(false, params);
+ InputStream input = createCipherInputStream(ciphertext, cipher);
+ try
+ {
+ while (input.read() >= 0)
+ {
+ }
+ fail("Expected invalid ciphertext after tamper and read : " + cipher.getAlgorithmName());
+ }
+ catch (InvalidCipherTextIOException e)
+ {
+ // Expected
+ }
+ try
+ {
+ input.close();
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception after tamper and read : " + cipher.getAlgorithmName());
+ }
+ }
+
+ /**
+ * Test truncation of ciphertext to make tag calculation impossible, followed by read from
+ * decrypting CipherInputStream
+ */
+ private void testTruncatedRead(AEADBlockCipher cipher, CipherParameters params)
+ throws Exception
+ {
+ cipher.init(true, params);
+
+ byte[] ciphertext = new byte[cipher.getOutputSize(streamSize)];
+ cipher.doFinal(ciphertext, cipher.processBytes(new byte[streamSize], 0, streamSize, ciphertext, 0));
+
+ // Truncate to just smaller than complete tag
+ byte[] truncated = new byte[ciphertext.length - streamSize - 1];
+ System.arraycopy(ciphertext, 0, truncated, 0, truncated.length);
+
+ cipher.init(false, params);
+ InputStream input = createCipherInputStream(truncated, cipher);
+ while (true)
+ {
+ int read = 0;
+ try
+ {
+ read = input.read();
+ }
+ catch (InvalidCipherTextIOException e)
+ {
+ // Expected
+ break;
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception on truncated read : " + cipher.getAlgorithmName());
+ break;
+ }
+ if (read < 0)
+ {
+ fail("Expected invalid ciphertext after truncate and read : " + cipher.getAlgorithmName());
+ break;
+ }
+ }
+ try
+ {
+ input.close();
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception after truncate and read : " + cipher.getAlgorithmName());
+ }
+ }
+
+ /**
+ * Test tampering of ciphertext followed by write to decrypting CipherOutputStream
+ */
+ private void testTamperedWrite(AEADBlockCipher cipher, CipherParameters params)
+ throws Exception
+ {
+ cipher.init(true, params);
+
+ byte[] ciphertext = new byte[cipher.getOutputSize(streamSize)];
+ cipher.doFinal(ciphertext, cipher.processBytes(new byte[streamSize], 0, streamSize, ciphertext, 0));
+
+ // Tamper
+ ciphertext[0] += 1;
+
+ cipher.init(false, params);
+ ByteArrayOutputStream plaintext = new ByteArrayOutputStream();
+ OutputStream output = createCipherOutputStream(plaintext, cipher);
+
+ for (int i = 0; i < ciphertext.length; i++)
+ {
+ output.write(ciphertext[i]);
+ }
+ try
+ {
+ output.close();
+ fail("Expected invalid ciphertext after tamper and write : " + cipher.getAlgorithmName());
+ }
+ catch (InvalidCipherTextIOException e)
+ {
+ // Expected
+ }
+ }
+
+ /**
+ * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE
+ */
+ private void testWriteRead(Object cipher, CipherParameters params, boolean blocks)
+ throws Exception
+ {
+ byte[] data = new byte[streamSize];
+ for (int i = 0; i < data.length; i++)
+ {
+ data[i] = (byte)(i % 255);
+ }
+
+ testWriteRead(cipher, params, blocks, data);
+ }
+
+ /**
+ * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE
+ */
+ private void testWriteReadEmpty(Object cipher, CipherParameters params, boolean blocks)
+ throws Exception
+ {
+ byte[] data = new byte[0];
+
+ testWriteRead(cipher, params, blocks, data);
+ }
+
+ private void testWriteRead(Object cipher, CipherParameters params, boolean blocks, byte[] data)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ init(cipher, true, params);
+
+ OutputStream cOut = createCipherOutputStream(bOut, cipher);
+ if (blocks)
+ {
+ int chunkSize = Math.max(1, data.length / 8);
+ for (int i = 0; i < data.length; i += chunkSize)
+ {
+ cOut.write(data, i, Math.min(chunkSize, data.length - i));
+ }
+ }
+ else
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ cOut.write(data[i]);
+ }
+ }
+ cOut.close();
+
+ byte[] cipherText = bOut.toByteArray();
+ bOut.reset();
+ init(cipher, false, params);
+ InputStream cIn = createCipherInputStream(cipherText, cipher);
+
+ if (blocks)
+ {
+ byte[] block = new byte[getBlockSize(cipher) + 1];
+ int c;
+ while ((c = cIn.read(block)) >= 0)
+ {
+ bOut.write(block, 0, c);
+ }
+ }
+ else
+ {
+ int c;
+ while ((c = cIn.read()) >= 0)
+ {
+ bOut.write(c);
+ }
+
+ }
+ cIn.close();
+
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception " + getName(cipher), e);
+ }
+
+ byte[] decrypted = bOut.toByteArray();
+ if (!Arrays.areEqual(data, decrypted))
+ {
+ fail("Failed - decrypted data doesn't match: " + getName(cipher));
+ }
+ }
+
+ private String getName(Object cipher)
+ {
+ if (cipher instanceof BufferedBlockCipher)
+ {
+ return ((BufferedBlockCipher)cipher).getUnderlyingCipher().getAlgorithmName();
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ return ((AEADBlockCipher)cipher).getUnderlyingCipher().getAlgorithmName();
+ }
+ else if (cipher instanceof StreamCipher)
+ {
+ return ((StreamCipher)cipher).getAlgorithmName();
+ }
+ return null;
+ }
+
+ private int getBlockSize(Object cipher)
+ {
+ if (cipher instanceof BlockCipher)
+ {
+ return ((BlockCipher)cipher).getBlockSize();
+ }
+ else if (cipher instanceof BufferedBlockCipher)
+ {
+ return ((BufferedBlockCipher)cipher).getBlockSize();
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ return ((AEADBlockCipher)cipher).getUnderlyingCipher().getBlockSize();
+ }
+ else if (cipher instanceof StreamCipher)
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ private void init(Object cipher, boolean forEncrypt, CipherParameters params)
+ {
+ if (cipher instanceof BufferedBlockCipher)
+ {
+ ((BufferedBlockCipher)cipher).init(forEncrypt, params);
+ }
+ else if (cipher instanceof AEADBlockCipher)
+ {
+ ((AEADBlockCipher)cipher).init(forEncrypt, params);
+ }
+ else if (cipher instanceof StreamCipher)
+ {
+ ((StreamCipher)cipher).init(forEncrypt, params);
+ }
+ }
+
+ protected void fail(String message, boolean authenticated, boolean bc)
+ {
+ if (bc || !authenticated)
+ {
+ super.fail(message);
+ }
+ else
+ {
+ // javax.crypto.CipherInputStream/CipherOutputStream
+ // are broken wrt handling AEAD failures
+ System.err.println("Broken JCE Streams: " + message);
+ }
+ }
+
+ /**
+ * Test CipherInputStream in ENCRYPT_MODE, CipherOutputStream in DECRYPT_MODE
+ */
+ private void testReadWrite(Object cipher, CipherParameters params, boolean blocks)
+ throws Exception
+ {
+ String lCode = "ABCDEFGHIJKLMNOPQRSTU";
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ try
+ {
+ init(cipher, true, params);
+
+ InputStream cIn = createCipherInputStream(lCode.getBytes(), cipher);
+ ByteArrayOutputStream ct = new ByteArrayOutputStream();
+
+ if (blocks)
+ {
+ byte[] block = new byte[getBlockSize(cipher) + 1];
+ int c;
+ while ((c = cIn.read(block)) >= 0)
+ {
+ ct.write(block, 0, c);
+ }
+ }
+ else
+ {
+ int c;
+ while ((c = cIn.read()) >= 0)
+ {
+ ct.write(c);
+ }
+ }
+ cIn.close();
+
+ init(cipher, false, params);
+ ByteArrayInputStream dataIn = new ByteArrayInputStream(ct.toByteArray());
+ OutputStream cOut = createCipherOutputStream(bOut, cipher);
+
+ if (blocks)
+ {
+ byte[] block = new byte[getBlockSize(cipher) + 1];
+ int c;
+ while ((c = dataIn.read(block)) >= 0)
+ {
+ cOut.write(block, 0, c);
+ }
+ }
+ else
+ {
+ int c;
+ while ((c = dataIn.read()) >= 0)
+ {
+ cOut.write(c);
+ }
+ }
+ cOut.flush();
+ cOut.close();
+
+ }
+ catch (Exception e)
+ {
+ fail("Unexpected exception " + getName(cipher), e);
+ }
+
+ String res = new String(bOut.toByteArray());
+ if (!res.equals(lCode))
+ {
+ fail("Failed read/write - decrypted data doesn't match: " + getName(cipher), lCode, res);
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ int[] testSizes = new int[]{0, 1, 7, 8, 9, 15, 16, 17, 1023, 1024, 1025, 2047, 2048, 2049, 4095, 4096, 4097};
+ for (int i = 0; i < testSizes.length; i++)
+ {
+ this.streamSize = testSizes[i];
+ performTests();
+ }
+ }
+
+ private void performTests()
+ throws Exception
+ {
+ testModes(new BlowfishEngine(), new BlowfishEngine(), 16);
+ testModes(new DESEngine(), new DESEngine(), 8);
+ testModes(new DESedeEngine(), new DESedeEngine(), 24);
+ testModes(new TEAEngine(), new TEAEngine(), 16);
+ testModes(new CAST5Engine(), new CAST5Engine(), 16);
+ testModes(new RC2Engine(), new RC2Engine(), 16);
+ testModes(new XTEAEngine(), new XTEAEngine(), 16);
+
+ testModes(new AESEngine(), new AESEngine(), 16);
+ testModes(new NoekeonEngine(), new NoekeonEngine(), 16);
+ testModes(new TwofishEngine(), new TwofishEngine(), 16);
+ testModes(new CAST6Engine(), new CAST6Engine(), 16);
+ testModes(new SEEDEngine(), new SEEDEngine(), 16);
+ testModes(new SerpentEngine(), new SerpentEngine(), 16);
+ testModes(new RC6Engine(), new RC6Engine(), 16);
+ testModes(new CamelliaEngine(), new CamelliaEngine(), 16);
+ testModes(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
+ new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512), 64);
+
+ testMode(new RC4Engine(), new KeyParameter(new byte[16]));
+ testMode(new Salsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
+ testMode(new XSalsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[32]), new byte[24]));
+ testMode(new ChaChaEngine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
+ testMode(new Grainv1Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
+ testMode(new Grain128Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[12]));
+ testMode(new HC128Engine(), new KeyParameter(new byte[16]));
+ testMode(new HC256Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+
+ testSkipping(new Salsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
+ testSkipping(new SICBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
+ }
+
+ private void testModes(BlockCipher cipher1, BlockCipher cipher2, int keySize)
+ throws Exception
+ {
+ final KeyParameter key = new KeyParameter(new byte[keySize]);
+ final int blockSize = getBlockSize(cipher1);
+ final CipherParameters withIv = new ParametersWithIV(key, new byte[blockSize]);
+
+ if (blockSize > 1)
+ {
+ testMode(new PaddedBufferedBlockCipher(cipher1, new PKCS7Padding()), key);
+
+ testMode(new PaddedBufferedBlockCipher(new CBCBlockCipher(cipher1), new PKCS7Padding()), withIv);
+
+ testMode(new BufferedBlockCipher(new OFBBlockCipher(cipher1, blockSize)), withIv);
+ testMode(new BufferedBlockCipher(new CFBBlockCipher(cipher1, blockSize)), withIv);
+ testMode(new BufferedBlockCipher(new SICBlockCipher(cipher1)), withIv);
+ }
+ // CTS requires at least one block
+ if (blockSize <= 16 && streamSize >= blockSize)
+ {
+ testMode(new CTSBlockCipher(cipher1), key);
+ }
+ if (blockSize <= 16 && streamSize >= blockSize)
+ {
+ testMode(new NISTCTSBlockCipher(NISTCTSBlockCipher.CS1, cipher1), key);
+ testMode(new NISTCTSBlockCipher(NISTCTSBlockCipher.CS2, cipher1), key);
+ testMode(new NISTCTSBlockCipher(NISTCTSBlockCipher.CS3, cipher1), key);
+ }
+ if (blockSize == 8 || blockSize == 16)
+ {
+ testMode(new EAXBlockCipher(cipher1), withIv);
+ }
+ if (blockSize == 16)
+ {
+ testMode(new CCMBlockCipher(cipher1), new ParametersWithIV(key, new byte[7]));
+ testMode(new GCMBlockCipher(cipher1), withIv);
+ testMode(new OCBBlockCipher(cipher1, cipher2), new ParametersWithIV(key, new byte[15]));
+ }
+ }
+
+ private void testSkipping(StreamCipher cipher, CipherParameters params)
+ throws Exception
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ init(cipher, true, params);
+
+ OutputStream cOut = createCipherOutputStream(bOut, cipher);
+ byte[] data = new byte[5000];
+
+ new SecureRandom().nextBytes(data);
+
+ cOut.write(data);
+
+ cOut.close();
+
+ init(cipher, false, params);
+
+ InputStream cIn = createCipherInputStream(bOut.toByteArray(), cipher);
+
+ long skip = cIn.skip(50);
+ if (skip != 50)
+ {
+ fail("wrong number of bytes skipped: " + skip);
+ }
+
+ byte[] block = new byte[50];
+
+ cIn.read(block);
+
+ if (!areEqual(data, 50, block, 0))
+ {
+ fail("initial skip mismatch");
+ }
+
+ skip = cIn.skip(3000);
+ if (skip != 3000)
+ {
+ fail("wrong number of bytes skipped: " + skip);
+ }
+
+ cIn.read(block);
+
+ if (!areEqual(data, 3100, block, 0))
+ {
+ fail("second skip mismatch");
+ }
+
+ cipher.reset();
+
+ cIn = createCipherInputStream(bOut.toByteArray(), cipher);
+ if (!cIn.markSupported())
+ {
+ fail("marking not supported");
+ }
+
+ cIn.mark(100);
+
+ cIn.read(block);
+
+ if (!areEqual(data, 0, block, 0))
+ {
+ fail("initial mark read failed");
+ }
+
+ cIn.reset();
+
+ cIn.read(block);
+
+ if (!areEqual(data, 0, block, 0))
+ {
+ fail(cipher.getAlgorithmName() + " initial reset read failed");
+ }
+
+ cIn.reset();
+
+ cIn.read(block);
+
+ cIn.mark(100);
+
+ cIn.read(block);
+
+ if (!areEqual(data, 50, block, 0))
+ {
+ fail("second mark read failed");
+ }
+
+ cIn.reset();
+
+ cIn.read(block);
+
+ if (!areEqual(data, 50, block, 0))
+ {
+ fail(cipher.getAlgorithmName() + " second reset read failed");
+ }
+
+ cIn.mark(3000);
+
+ skip = cIn.skip(2050);
+ if (skip != 2050)
+ {
+ fail("wrong number of bytes skipped: " + skip);
+ }
+
+ cIn.reset();
+
+ cIn.read(block);
+
+ if (!areEqual(data, 100, block, 0))
+ {
+ fail(cipher.getAlgorithmName() + " third reset read failed");
+ }
+
+ cIn.read(new byte[2150]);
+
+ cIn.reset();
+
+ cIn.read(block);
+
+ if (!areEqual(data, 100, block, 0))
+ {
+ fail(cipher.getAlgorithmName() + " fourth reset read failed");
+ }
+
+ cIn.close();
+ }
+
+ private boolean areEqual(byte[] a, int aOff, byte[] b, int bOff)
+ {
+ for (int i = bOff; i != b.length; i++)
+ {
+ if (a[aOff + i - bOff] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new CipherStreamTest());
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CipherTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CipherTest.java
new file mode 100644
index 00000000..407a0440
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CipherTest.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.test.SimpleTest;
+
+public abstract class CipherTest
+ extends SimpleTest
+{
+ private SimpleTest[] _tests;
+ private BlockCipher _engine;
+ private KeyParameter _validKey;
+
+// protected CipherTest(
+// SimpleTest[] tests)
+// {
+// _tests = tests;
+// }
+
+ protected CipherTest(
+ SimpleTest[] tests,
+ BlockCipher engine,
+ KeyParameter validKey)
+ {
+ _tests = tests;
+ _engine = engine;
+ _validKey = validKey;
+ }
+
+ public abstract String getName();
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i != _tests.length; i++)
+ {
+ _tests[i].performTest();
+ }
+
+ if (_engine != null)
+ {
+ //
+ // state tests
+ //
+ byte[] buf = new byte[128];
+
+ try
+ {
+ _engine.processBlock(buf, 0, buf, 0);
+
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+
+ bufferSizeCheck((_engine));
+ }
+ }
+
+ private void bufferSizeCheck(
+ BlockCipher engine)
+ {
+ byte[] correctBuf = new byte[engine.getBlockSize()];
+ byte[] shortBuf = new byte[correctBuf.length / 2];
+
+ engine.init(true, _validKey);
+
+ try
+ {
+ engine.processBlock(shortBuf, 0, correctBuf, 0);
+
+ fail("failed short input check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ engine.processBlock(correctBuf, 0, shortBuf, 0);
+
+ fail("failed short output check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ engine.init(false, _validKey);
+
+ try
+ {
+ engine.processBlock(shortBuf, 0, correctBuf, 0);
+
+ fail("failed short input check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ engine.processBlock(correctBuf, 0, shortBuf, 0);
+
+ fail("failed short output check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/CramerShoupTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/CramerShoupTest.java
new file mode 100644
index 00000000..e9633572
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/CramerShoupTest.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.agreement.DHStandardGroups;
+import org.bouncycastle.crypto.engines.CramerShoupCiphertext;
+import org.bouncycastle.crypto.engines.CramerShoupCoreEngine;
+import org.bouncycastle.crypto.engines.CramerShoupCoreEngine.CramerShoupCiphertextException;
+import org.bouncycastle.crypto.generators.CramerShoupKeyPairGenerator;
+import org.bouncycastle.crypto.generators.CramerShoupParametersGenerator;
+import org.bouncycastle.crypto.params.CramerShoupKeyGenerationParameters;
+import org.bouncycastle.crypto.params.CramerShoupParameters;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class CramerShoupTest
+ extends SimpleTest
+{
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ private static final SecureRandom RND = new SecureRandom();
+
+ private AsymmetricCipherKeyPair keyPair;
+
+ public static void main(String[] args)
+ {
+ runTest(new CramerShoupTest());
+ }
+
+ public String getName()
+ {
+ return "CramerShoup";
+ }
+
+
+ public void performTest()
+ throws Exception
+ {
+ BigInteger pSubOne = DHStandardGroups.rfc3526_2048.getP().subtract(ONE);
+ for (int i = 0; i < 10; ++i)
+ {
+ BigInteger message = BigIntegers.createRandomInRange(ONE, pSubOne, RND);
+
+ BigInteger m1 = encDecTest(message);
+ BigInteger m2 = labelledEncDecTest(message, "myRandomLabel");
+ BigInteger m3 = encDecEncodingTest(message);
+ BigInteger m4 = labelledEncDecEncodingTest(message, "myOtherCoolLabel");
+
+ if (!message.equals(m1) || !message.equals(m2) || !message.equals(m3) || !message.equals(m4))
+ {
+ fail("decrypted message != original message");
+ }
+ }
+ }
+
+ private BigInteger encDecEncodingTest(BigInteger m)
+ {
+ CramerShoupCiphertext ciphertext = encrypt(m);
+ byte[] c = ciphertext.toByteArray();
+ CramerShoupCiphertext decC = new CramerShoupCiphertext(c);
+ return decrypt(decC);
+ }
+
+ private BigInteger labelledEncDecEncodingTest(BigInteger m, String l)
+ {
+ byte[] c = encrypt(m, l).toByteArray();
+ return decrypt(new CramerShoupCiphertext(c), l);
+ }
+
+ private BigInteger encDecTest(BigInteger m)
+ {
+ CramerShoupCiphertext c = encrypt(m);
+ return decrypt(c);
+ }
+
+ private BigInteger labelledEncDecTest(BigInteger m, String l)
+ {
+ CramerShoupCiphertext c = encrypt(m, l);
+ return decrypt(c, l);
+ }
+
+
+ private BigInteger decrypt(CramerShoupCiphertext ciphertext)
+ {
+ return decrypt(ciphertext, null);
+ }
+
+ private BigInteger decrypt(CramerShoupCiphertext ciphertext, String label)
+ {
+
+ CramerShoupCoreEngine engine = new CramerShoupCoreEngine();
+ if (label != null)
+ {
+ engine.init(false, keyPair.getPrivate(), label);
+ }
+ else
+ {
+ engine.init(false, keyPair.getPrivate());
+ }
+ try
+ {
+ BigInteger m = engine.decryptBlock(ciphertext);
+
+ return m;
+ }
+ catch (CramerShoupCiphertextException e)
+ {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ private CramerShoupCiphertext encrypt(BigInteger message)
+ {
+ return encrypt(message, null);
+ }
+
+ private CramerShoupCiphertext encrypt(BigInteger message, String label)
+ {
+ CramerShoupKeyPairGenerator kpGen = new CramerShoupKeyPairGenerator();
+ CramerShoupParametersGenerator pGen = new CramerShoupParametersGenerator();
+
+ pGen.init(2048, 1, RND);
+ CramerShoupParameters params = pGen.generateParameters(DHStandardGroups.rfc3526_2048);
+ CramerShoupKeyGenerationParameters param = new CramerShoupKeyGenerationParameters(RND, params);
+
+ kpGen.init(param);
+ keyPair = kpGen.generateKeyPair();
+
+ CramerShoupCoreEngine engine = new CramerShoupCoreEngine();
+ if (label != null)
+ {
+ engine.init(true, keyPair.getPublic(), label);
+ }
+ else
+ {
+ engine.init(true, keyPair.getPublic());
+ }
+
+ CramerShoupCiphertext ciphertext = engine.encryptBlock(message);
+
+ return ciphertext;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DESTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DESTest.java
new file mode 100644
index 00000000..08056fc4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DESTest.java
@@ -0,0 +1,206 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.generators.DESKeyGenerator;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.crypto.params.DESParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+import java.security.SecureRandom;
+
+class DESParityTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "DESParityTest";
+ }
+
+ public void performTest()
+ {
+ byte[] k1In = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff,
+ (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
+ byte[] k1Out = { (byte)0xfe, (byte)0xfe, (byte)0xfe, (byte)0xfe,
+ (byte)0xfe, (byte)0xfe, (byte)0xfe, (byte)0xfe };
+
+ byte[] k2In = { (byte)0xef, (byte)0xcb, (byte)0xda, (byte)0x4f,
+ (byte)0xaa, (byte)0x99, (byte)0x7f, (byte)0x63 };
+ byte[] k2Out = { (byte)0xef, (byte)0xcb, (byte)0xda, (byte)0x4f,
+ (byte)0xab, (byte)0x98, (byte)0x7f, (byte)0x62 };
+
+ DESParameters.setOddParity(k1In);
+
+ for (int i = 0; i != k1In.length; i++)
+ {
+ if (k1In[i] != k1Out[i])
+ {
+ fail("Failed "
+ + "got " + new String(Hex.encode(k1In))
+ + " expected " + new String(Hex.encode(k1Out)));
+ }
+ }
+
+ DESParameters.setOddParity(k2In);
+
+ for (int i = 0; i != k2In.length; i++)
+ {
+ if (k2In[i] != k2Out[i])
+ {
+ fail("Failed "
+ + "got " + new String(Hex.encode(k2In))
+ + " expected " + new String(Hex.encode(k2Out)));
+ }
+ }
+ }
+}
+
+class KeyGenTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "KeyGenTest";
+ }
+
+ public void performTest()
+ {
+ DESKeyGenerator keyGen = new DESKeyGenerator();
+
+ keyGen.init(new KeyGenerationParameters(new SecureRandom(), 56));
+
+ byte[] kB = keyGen.generateKey();
+
+ if (kB.length != 8)
+ {
+ fail("DES bit key wrong length.");
+ }
+ }
+}
+
+class DESParametersTest
+ extends SimpleTest
+{
+ static private byte[] weakKeys =
+ {
+ (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,
+ (byte)0x1f,(byte)0x1f,(byte)0x1f,(byte)0x1f, (byte)0x0e,(byte)0x0e,(byte)0x0e,(byte)0x0e,
+ (byte)0xe0,(byte)0xe0,(byte)0xe0,(byte)0xe0, (byte)0xf1,(byte)0xf1,(byte)0xf1,(byte)0xf1,
+ (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe, (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe,
+ /* semi-weak keys */
+ (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe, (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe,
+ (byte)0x1f,(byte)0xe0,(byte)0x1f,(byte)0xe0, (byte)0x0e,(byte)0xf1,(byte)0x0e,(byte)0xf1,
+ (byte)0x01,(byte)0xe0,(byte)0x01,(byte)0xe0, (byte)0x01,(byte)0xf1,(byte)0x01,(byte)0xf1,
+ (byte)0x1f,(byte)0xfe,(byte)0x1f,(byte)0xfe, (byte)0x0e,(byte)0xfe,(byte)0x0e,(byte)0xfe,
+ (byte)0x01,(byte)0x1f,(byte)0x01,(byte)0x1f, (byte)0x01,(byte)0x0e,(byte)0x01,(byte)0x0e,
+ (byte)0xe0,(byte)0xfe,(byte)0xe0,(byte)0xfe, (byte)0xf1,(byte)0xfe,(byte)0xf1,(byte)0xfe,
+ (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01, (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01,
+ (byte)0xe0,(byte)0x1f,(byte)0xe0,(byte)0x1f, (byte)0xf1,(byte)0x0e,(byte)0xf1,(byte)0x0e,
+ (byte)0xe0,(byte)0x01,(byte)0xe0,(byte)0x01, (byte)0xf1,(byte)0x01,(byte)0xf1,(byte)0x01,
+ (byte)0xfe,(byte)0x1f,(byte)0xfe,(byte)0x1f, (byte)0xfe,(byte)0x0e,(byte)0xfe,(byte)0x0e,
+ (byte)0x1f,(byte)0x01,(byte)0x1f,(byte)0x01, (byte)0x0e,(byte)0x01,(byte)0x0e,(byte)0x01,
+ (byte)0xfe,(byte)0xe0,(byte)0xfe,(byte)0xe0, (byte)0xfe,(byte)0xf1,(byte)0xfe,(byte)0xf1
+ };
+
+ public String getName()
+ {
+ return "DESParameters";
+ }
+
+ public void performTest() throws Exception
+ {
+ try
+ {
+ DESParameters.isWeakKey(new byte[4], 0);
+ fail("no exception on small key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("key material too short."))
+ {
+ fail("wrong exception");
+ }
+ }
+
+ try
+ {
+ new DESParameters(weakKeys);
+ fail("no exception on weak key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("attempt to create weak DES key"))
+ {
+ fail("wrong exception");
+ }
+ }
+
+ for (int i = 0; i != weakKeys.length; i += 8)
+ {
+ if (!DESParameters.isWeakKey(weakKeys, i))
+ {
+ fail("weakKey test failed");
+ }
+ }
+ }
+}
+
+/**
+ * DES tester - vectors from <a href=http://www.itl.nist.gov/fipspubs/fip81.htm>FIPS 81</a>
+ */
+public class DESTest
+ extends CipherTest
+{
+ static String input1 = "4e6f77206973207468652074696d6520666f7220616c6c20";
+ static String input2 = "4e6f7720697320746865";
+ static String input3 = "4e6f7720697320746865aabbcc";
+
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new DESEngine(),
+ new KeyParameter(Hex.decode("0123456789abcdef")),
+ input1, "3fa40e8a984d48156a271787ab8883f9893d51ec4b563b53"),
+ new BlockCipherVectorTest(1, new CBCBlockCipher(new DESEngine()),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input1, "e5c7cdde872bf27c43e934008c389c0f683788499a7c05f6"),
+ new BlockCipherVectorTest(2, new CFBBlockCipher(new DESEngine(), 8),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input2, "f31fda07011462ee187f"),
+ new BlockCipherVectorTest(3, new CFBBlockCipher(new DESEngine(), 64),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input1, "f3096249c7f46e51a69e839b1a92f78403467133898ea622"),
+ new BlockCipherVectorTest(4, new OFBBlockCipher(new DESEngine(), 8),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input2, "f34a2850c9c64985d684"),
+ new BlockCipherVectorTest(5, new CFBBlockCipher(new DESEngine(), 64),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input3, "f3096249c7f46e51a69e0954bf"),
+ new BlockCipherVectorTest(6, new OFBBlockCipher(new DESEngine(), 64),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0123456789abcdef")), Hex.decode("1234567890abcdef")),
+ input3, "f3096249c7f46e5135f2c0eb8b"),
+ new DESParityTest(),
+ new DESParametersTest(),
+ new KeyGenTest()
+ };
+
+ public DESTest()
+ {
+ super(tests, new DESEngine(), new KeyParameter(new byte[8]));
+ }
+
+ public String getName()
+ {
+ return "DES";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DESTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DESedeTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DESedeTest.java
new file mode 100644
index 00000000..b14897fa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DESedeTest.java
@@ -0,0 +1,177 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.DESedeWrapEngine;
+import org.bouncycastle.crypto.generators.DESedeKeyGenerator;
+import org.bouncycastle.crypto.params.DESedeParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+import java.security.SecureRandom;
+
+/**
+ * DESede tester
+ */
+public class DESedeTest
+ extends CipherTest
+{
+ static private byte[] weakKey = // first 8 bytes non-weak
+ {
+ (byte)0x06,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,
+ (byte)0x1f,(byte)0x1f,(byte)0x1f,(byte)0x1f, (byte)0x0e,(byte)0x0e,(byte)0x0e,(byte)0x0e,
+ (byte)0xe0,(byte)0xe0,(byte)0xe0,(byte)0xe0, (byte)0xf1,(byte)0xf1,(byte)0xf1,(byte)0xf1,
+ };
+
+ static String input1 = "4e6f77206973207468652074696d6520666f7220616c6c20";
+ static String input2 = "4e6f7720697320746865";
+
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new DESedeEngine(),
+ new DESedeParameters(Hex.decode("0123456789abcdef0123456789abcdef")),
+ input1, "3fa40e8a984d48156a271787ab8883f9893d51ec4b563b53"),
+ new BlockCipherVectorTest(1, new DESedeEngine(),
+ new DESedeParameters(Hex.decode("0123456789abcdeffedcba9876543210")),
+ input1, "d80a0d8b2bae5e4e6a0094171abcfc2775d2235a706e232c"),
+ new BlockCipherVectorTest(2, new DESedeEngine(),
+ new DESedeParameters(Hex.decode("0123456789abcdef0123456789abcdef0123456789abcdef")),
+ input1, "3fa40e8a984d48156a271787ab8883f9893d51ec4b563b53"),
+ new BlockCipherVectorTest(3, new DESedeEngine(),
+ new DESedeParameters(Hex.decode("0123456789abcdeffedcba98765432100123456789abcdef")),
+ input1, "d80a0d8b2bae5e4e6a0094171abcfc2775d2235a706e232c")
+ };
+
+ DESedeTest()
+ {
+ super(tests, new DESedeEngine(), new KeyParameter(new byte[16]));
+ }
+
+ private void wrapTest(
+ int id,
+ byte[] kek,
+ byte[] iv,
+ byte[] in,
+ byte[] out)
+ {
+ Wrapper wrapper = new DESedeWrapEngine();
+
+ wrapper.init(true, new ParametersWithIV(new KeyParameter(kek), iv));
+
+ try
+ {
+ byte[] cText = wrapper.wrap(in, 0, in.length);
+ if (!areEqual(cText, out))
+ {
+ fail(": failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+ }
+ catch (Exception e)
+ {
+ fail("failed wrap test exception: " + e.toString(), e);
+ }
+
+ wrapper.init(false, new KeyParameter(kek));
+
+ try
+ {
+ byte[] pText = wrapper.unwrap(out, 0, out.length);
+ if (!areEqual(pText, in))
+ {
+ fail("failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+ catch (Exception e)
+ {
+ fail("failed unwrap test exception: " + e.toString(), e);
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ byte[] kek1 = Hex.decode("255e0d1c07b646dfb3134cc843ba8aa71f025b7c0838251f");
+ byte[] iv1 = Hex.decode("5dd4cbfc96f5453b");
+ byte[] in1 = Hex.decode("2923bf85e06dd6ae529149f1f1bae9eab3a7da3d860d3e98");
+ byte[] out1 = Hex.decode("690107618ef092b3b48ca1796b234ae9fa33ebb4159604037db5d6a84eb3aac2768c632775a467d4");
+
+ wrapTest(1, kek1, iv1, in1, out1);
+
+ //
+ // key generation
+ //
+ SecureRandom random = new SecureRandom();
+ DESedeKeyGenerator keyGen = new DESedeKeyGenerator();
+
+ keyGen.init(new KeyGenerationParameters(random, 112));
+
+ byte[] kB = keyGen.generateKey();
+
+ if (kB.length != 16)
+ {
+ fail("112 bit key wrong length.");
+ }
+
+ keyGen.init(new KeyGenerationParameters(random, 168));
+
+ kB = keyGen.generateKey();
+
+ if (kB.length != 24)
+ {
+ fail("168 bit key wrong length.");
+ }
+
+ try
+ {
+ keyGen.init(new KeyGenerationParameters(random, 200));
+
+ fail("invalid key length not detected.");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ DESedeParameters.isWeakKey(new byte[4], 0);
+ fail("no exception on small key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("key material too short."))
+ {
+ fail("wrong exception");
+ }
+ }
+
+ try
+ {
+ new DESedeParameters(weakKey);
+ fail("no exception on weak key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().equals("attempt to create weak DESede key"))
+ {
+ fail("wrong exception");
+ }
+ }
+ }
+
+ public String getName()
+ {
+ return "DESede";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DESedeTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DHKEKGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DHKEKGeneratorTest.java
new file mode 100644
index 00000000..8a83d2a3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DHKEKGeneratorTest.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.agreement.kdf.DHKDFParameters;
+import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * DHKEK Generator tests - from RFC 2631.
+ */
+public class DHKEKGeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("000102030405060708090a0b0c0d0e0f10111213");
+ private ASN1ObjectIdentifier alg1 = PKCSObjectIdentifiers.id_alg_CMS3DESwrap;
+ private byte[] result1 = Hex.decode("a09661392376f7044d9052a397883246b67f5f1ef63eb5fb");
+
+ private byte[] seed2 = Hex.decode("000102030405060708090a0b0c0d0e0f10111213");
+ private ASN1ObjectIdentifier alg2 = PKCSObjectIdentifiers.id_alg_CMSRC2wrap;
+ private byte[] partyAInfo = Hex.decode(
+ "0123456789abcdeffedcba9876543201"
+ + "0123456789abcdeffedcba9876543201"
+ + "0123456789abcdeffedcba9876543201"
+ + "0123456789abcdeffedcba9876543201");
+ private byte[] result2 = Hex.decode("48950c46e0530075403cce72889604e0");
+
+ public DHKEKGeneratorTest()
+ {
+ }
+
+ public void performTest()
+ {
+ checkMask(1, new DHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg1, 192, seed1), result1);
+ checkMask(2, new DHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg2, 128, seed2, partyAInfo), result2);
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ DerivationParameters params,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(params);
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("DHKEKGenerator failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "DHKEKGenerator";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DHKEKGeneratorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DHTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DHTest.java
new file mode 100644
index 00000000..862b9f25
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DHTest.java
@@ -0,0 +1,414 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.agreement.DHAgreement;
+import org.bouncycastle.crypto.agreement.DHBasicAgreement;
+import org.bouncycastle.crypto.generators.DHBasicKeyPairGenerator;
+import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
+import org.bouncycastle.crypto.generators.DHParametersGenerator;
+import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class DHTest
+ extends SimpleTest
+{
+ private BigInteger g512 = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16);
+ private BigInteger p512 = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16);
+
+ private BigInteger g768 = new BigInteger("7c240073c1316c621df461b71ebb0cdcc90a6e5527e5e126633d131f87461c4dc4afc60c2cb0f053b6758871489a69613e2a8b4c8acde23954c08c81cbd36132cfd64d69e4ed9f8e51ed6e516297206672d5c0a69135df0a5dcf010d289a9ca1", 16);
+ private BigInteger p768 = new BigInteger("8c9dd223debed1b80103b8b309715be009d48860ed5ae9b9d5d8159508efd802e3ad4501a7f7e1cfec78844489148cd72da24b21eddd01aa624291c48393e277cfc529e37075eccef957f3616f962d15b44aeab4039d01b817fde9eaa12fd73f", 16);
+
+ private BigInteger g1024 = new BigInteger("1db17639cdf96bc4eabba19454f0b7e5bd4e14862889a725c96eb61048dcd676ceb303d586e30f060dbafd8a571a39c4d823982117da5cc4e0f89c77388b7a08896362429b94a18a327604eb7ff227bffbc83459ade299e57b5f77b50fb045250934938efa145511166e3197373e1b5b1e52de713eb49792bedde722c6717abf", 16);
+ private BigInteger p1024 = new BigInteger("a00e283b3c624e5b2b4d9fbc2653b5185d99499b00fd1bf244c6f0bb817b4d1c451b2958d62a0f8a38caef059fb5ecd25d75ed9af403f5b5bdab97a642902f824e3c13789fed95fa106ddfe0ff4a707c85e2eb77d49e68f2808bcea18ce128b178cd287c6bc00efa9a1ad2a673fe0dceace53166f75b81d6709d5f8af7c66bb7", 16);
+
+ public String getName()
+ {
+ return "DH";
+ }
+
+ private void testDH(
+ int size,
+ BigInteger g,
+ BigInteger p)
+ {
+ DHKeyPairGenerator kpGen = getDHKeyPairGenerator(g, p);
+
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+ //
+ // generate second pair
+ //
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // two way
+ //
+ DHAgreement e1 = new DHAgreement();
+ DHAgreement e2 = new DHAgreement();
+
+ e1.init(pv1);
+ e2.init(pv2);
+
+ BigInteger m1 = e1.calculateMessage();
+ BigInteger m2 = e2.calculateMessage();
+
+ BigInteger k1 = e1.calculateAgreement(pu2, m2);
+ BigInteger k2 = e2.calculateAgreement(pu1, m1);
+
+ if (!k1.equals(k2))
+ {
+ fail(size + " bit 2-way test failed");
+ }
+ }
+
+ private void testDHBasic(
+ int size,
+ int privateValueSize,
+ BigInteger g,
+ BigInteger p)
+ {
+ DHBasicKeyPairGenerator kpGen = getDHBasicKeyPairGenerator(g, p, privateValueSize);
+
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ checkKeySize(privateValueSize, pv1);
+ //
+ // generate second pair
+ //
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ checkKeySize(privateValueSize, pv2);
+ //
+ // two way
+ //
+ DHBasicAgreement e1 = new DHBasicAgreement();
+ DHBasicAgreement e2 = new DHBasicAgreement();
+
+ e1.init(pv1);
+ e2.init(pv2);
+
+ BigInteger k1 = e1.calculateAgreement(pu2);
+ BigInteger k2 = e2.calculateAgreement(pu1);
+
+ if (!k1.equals(k2))
+ {
+ fail("basic " + size + " bit 2-way test failed");
+ }
+ }
+
+ private void checkKeySize(
+ int privateValueSize,
+ DHPrivateKeyParameters priv)
+ {
+ if (privateValueSize != 0)
+ {
+ if (priv.getX().bitLength() != privateValueSize)
+ {
+ fail("limited key check failed for key size " + privateValueSize);
+ }
+ }
+ }
+
+ private void testGPWithRandom(
+ DHKeyPairGenerator kpGen)
+ {
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+ //
+ // generate second pair
+ //
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // two way
+ //
+ DHAgreement e1 = new DHAgreement();
+ DHAgreement e2 = new DHAgreement();
+
+ e1.init(new ParametersWithRandom(pv1, new SecureRandom()));
+ e2.init(new ParametersWithRandom(pv2, new SecureRandom()));
+
+ BigInteger m1 = e1.calculateMessage();
+ BigInteger m2 = e2.calculateMessage();
+
+ BigInteger k1 = e1.calculateAgreement(pu2, m2);
+ BigInteger k2 = e2.calculateAgreement(pu1, m1);
+
+ if (!k1.equals(k2))
+ {
+ fail("basic with random 2-way test failed");
+ }
+ }
+
+ private void testSimpleWithRandom(
+ DHBasicKeyPairGenerator kpGen)
+ {
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+ //
+ // generate second pair
+ //
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // two way
+ //
+ DHBasicAgreement e1 = new DHBasicAgreement();
+ DHBasicAgreement e2 = new DHBasicAgreement();
+
+ e1.init(new ParametersWithRandom(pv1, new SecureRandom()));
+ e2.init(new ParametersWithRandom(pv2, new SecureRandom()));
+
+ BigInteger k1 = e1.calculateAgreement(pu2);
+ BigInteger k2 = e2.calculateAgreement(pu1);
+
+ if (!k1.equals(k2))
+ {
+ fail("basic with random 2-way test failed");
+ }
+ }
+
+ private DHBasicKeyPairGenerator getDHBasicKeyPairGenerator(
+ BigInteger g,
+ BigInteger p,
+ int privateValueSize)
+ {
+ DHParameters dhParams = new DHParameters(p, g, null, privateValueSize);
+ DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
+ DHBasicKeyPairGenerator kpGen = new DHBasicKeyPairGenerator();
+
+ kpGen.init(params);
+
+ return kpGen;
+ }
+
+ private DHKeyPairGenerator getDHKeyPairGenerator(
+ BigInteger g,
+ BigInteger p)
+ {
+ DHParameters dhParams = new DHParameters(p, g);
+ DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
+ DHKeyPairGenerator kpGen = new DHKeyPairGenerator();
+
+ kpGen.init(params);
+
+ return kpGen;
+ }
+
+ /**
+ * this test is can take quiet a while
+ */
+ private void testGeneration(
+ int size)
+ {
+ DHParametersGenerator pGen = new DHParametersGenerator();
+
+ pGen.init(size, 10, new SecureRandom());
+
+ DHParameters dhParams = pGen.generateParameters();
+
+ if (dhParams.getL() != 0)
+ {
+ fail("DHParametersGenerator failed to set J to 0 in generated DHParameters");
+ }
+
+ DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
+
+ DHBasicKeyPairGenerator kpGen = new DHBasicKeyPairGenerator();
+
+ kpGen.init(params);
+
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // generate second pair
+ //
+ params = new DHKeyGenerationParameters(new SecureRandom(), pu1.getParameters());
+
+ kpGen.init(params);
+
+ pair = kpGen.generateKeyPair();
+
+ DHPublicKeyParameters pu2 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv2 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ //
+ // two way
+ //
+ DHBasicAgreement e1 = new DHBasicAgreement();
+ DHBasicAgreement e2 = new DHBasicAgreement();
+
+ e1.init(new ParametersWithRandom(pv1, new SecureRandom()));
+ e2.init(new ParametersWithRandom(pv2, new SecureRandom()));
+
+ BigInteger k1 = e1.calculateAgreement(pu2);
+ BigInteger k2 = e2.calculateAgreement(pu1);
+
+ if (!k1.equals(k2))
+ {
+ fail("basic with " + size + " bit 2-way test failed");
+ }
+ }
+ private void testBounds()
+ {
+ BigInteger p1 = new BigInteger("00C8028E9151C6B51BCDB35C1F6B2527986A72D8546AE7A4BF41DC4289FF9837EE01592D36C324A0F066149B8B940C86C87D194206A39038AE3396F8E12435BB74449B70222D117B8A2BB77CB0D67A5D664DDE7B75E0FEC13CE0CAF258DAF3ADA0773F6FF0F2051D1859929AAA53B07809E496B582A89C3D7DA8B6E38305626621", 16);
+ BigInteger g1 = new BigInteger("1F869713181464577FE4026B47102FA0D7675503A4FCDA810881FAEC3524E6DBAEA9B96561EF7F8BEA76466DF11C2F3EB1A90CC5851735BF860606481257EECE6418C0204E61004E85D7131CE54BCBC7AD67E53C79DCB715E7C8D083DCD85D728283EC8F96839B4C9FA7C0727C472BEB94E4613CAFA8D580119C0AF4BF8AF252", 16);
+ int l1 = 1023;
+
+ BigInteger p2 = new BigInteger("00B333C98720220CC3946F494E25231B3E19F9AD5F6B19F4E7ABF80D8826C491C3224D4F7415A14A7C11D1BE584405FED12C3554F103E56A72D986CA5E325BB9DE07AC37D1EAE5E5AC724D32EF638F0E4462D4C1FC7A45B9FD3A5DF5EC36A1FA4DAA3FBB66AA42B1B71DF416AB547E987513426C7BB8634F5F4D37705514FDC1E1", 16);
+ BigInteger g2 = new BigInteger("2592F5A99FE46313650CCE66C94C15DBED9F4A45BD05C329986CF5D3E12139F0405A47C6385FEA27BFFEDC4CBABC5BB151F3BEE7CC3D51567F1E2B12A975AA9F48A70BDAAE7F5B87E70ADCF902490A3CBEFEDA41EBA8E12E02B56120B5FDEFBED07F5EAD3AE020DF3C8233216F8F0D35E13A7AE4DA5CBCC0D91EADBF20C281C6", 16);
+ int l2 = 1024;
+
+ DHKeyGenerationParameters params1 = new DHKeyGenerationParameters(new SecureRandom(), new DHParameters(p1, g1, null, l1));
+ DHKeyGenerationParameters params2 = new DHKeyGenerationParameters(new SecureRandom(), new DHParameters(p2, g2, null, l2));
+
+ DHBasicKeyPairGenerator kpGen = new DHBasicKeyPairGenerator();
+
+ kpGen.init(params1);
+ kpGen.init(params2);
+ }
+
+ public void performTest()
+ {
+ testDHBasic(512, 0, g512, p512);
+ testDHBasic(768, 0, g768, p768);
+ testDHBasic(1024, 0, g1024, p1024);
+
+ testDHBasic(512, 64, g512, p512);
+ testDHBasic(768, 128, g768, p768);
+ testDHBasic(1024, 256, g1024, p1024);
+
+ testDH(512, g512, p512);
+ testDH(768, g768, p768);
+ testDH(1024, g1024, p1024);
+
+ testBounds();
+
+ //
+ // generation test.
+ //
+ testGeneration(256);
+
+ //
+ // with random test
+ //
+ DHBasicKeyPairGenerator kpBasicGen = getDHBasicKeyPairGenerator(g512, p512, 0);
+
+ testSimpleWithRandom(kpBasicGen);
+
+ DHKeyPairGenerator kpGen = getDHKeyPairGenerator(g512, p512);
+
+ testGPWithRandom(kpGen);
+
+ //
+ // parameter tests
+ //
+ DHAgreement dh = new DHAgreement();
+ AsymmetricCipherKeyPair dhPair = kpGen.generateKeyPair();
+
+ try
+ {
+ dh.init(dhPair.getPublic());
+ fail("DHAgreement key check failed");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // ignore
+ }
+
+ DHKeyPairGenerator kpGen768 = getDHKeyPairGenerator(g768, p768);
+
+ try
+ {
+ dh.init(dhPair.getPrivate());
+
+ dh.calculateAgreement((DHPublicKeyParameters)kpGen768.generateKeyPair().getPublic(), BigInteger.valueOf(100));
+
+ fail("DHAgreement agreement check failed");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // ignore
+ }
+
+ DHBasicAgreement dhBasic = new DHBasicAgreement();
+ AsymmetricCipherKeyPair dhBasicPair = kpBasicGen.generateKeyPair();
+
+ try
+ {
+ dhBasic.init(dhBasicPair.getPublic());
+ fail("DHBasicAgreement key check failed");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ DHBasicKeyPairGenerator kpBasicGen768 = getDHBasicKeyPairGenerator(g768, p768, 0);
+
+ try
+ {
+ dhBasic.init(dhPair.getPrivate());
+
+ dhBasic.calculateAgreement((DHPublicKeyParameters)kpBasicGen768.generateKeyPair().getPublic());
+
+ fail("DHBasicAgreement agreement check failed");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DHTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DSATest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DSATest.java
new file mode 100644
index 00000000..02c748fa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DSATest.java
@@ -0,0 +1,602 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.digests.SHA224Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.generators.DSAKeyPairGenerator;
+import org.bouncycastle.crypto.generators.DSAParametersGenerator;
+import org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DSAParameterGenerationParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
+import org.bouncycastle.crypto.params.DSAValidationParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.DSASigner;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.FixedSecureRandom;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test based on FIPS 186-2, Appendix 5, an example of DSA, and FIPS 168-3 test vectors.
+ */
+public class DSATest
+ extends SimpleTest
+{
+ byte[] k1 = Hex.decode("d5014e4b60ef2ba8b6211b4062ba3224e0427dd3");
+ byte[] k2 = Hex.decode("345e8d05c075c3a508df729a1685690e68fcfb8c8117847e89063bca1f85d968fd281540b6e13bd1af989a1fbf17e06462bf511f9d0b140fb48ac1b1baa5bded");
+
+ SecureRandom random = new FixedSecureRandom(new byte[][] { k1, k2});
+
+ byte[] keyData = Hex.decode("b5014e4b60ef2ba8b6211b4062ba3224e0427dd3");
+
+ SecureRandom keyRandom = new FixedSecureRandom(new byte[][] { keyData, keyData });
+
+ BigInteger pValue = new BigInteger("8df2a494492276aa3d25759bb06869cbeac0d83afb8d0cf7cbb8324f0d7882e5d0762fc5b7210eafc2e9adac32ab7aac49693dfbf83724c2ec0736ee31c80291", 16);
+ BigInteger qValue = new BigInteger("c773218c737ec8ee993b4f2ded30f48edace915f", 16);
+
+ public String getName()
+ {
+ return "DSA";
+ }
+
+ public void performTest()
+ {
+ BigInteger r = new BigInteger("68076202252361894315274692543577577550894681403");
+ BigInteger s = new BigInteger("1089214853334067536215539335472893651470583479365");
+ DSAParametersGenerator pGen = new DSAParametersGenerator();
+
+ pGen.init(512, 80, random);
+
+ DSAParameters params = pGen.generateParameters();
+ DSAValidationParameters pValid = params.getValidationParameters();
+
+ if (pValid.getCounter() != 105)
+ {
+ fail("Counter wrong");
+ }
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ fail("p or q wrong");
+ }
+
+ DSAKeyPairGenerator dsaKeyGen = new DSAKeyPairGenerator();
+ DSAKeyGenerationParameters genParam = new DSAKeyGenerationParameters(keyRandom, params);
+
+ dsaKeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = dsaKeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), keyRandom);
+
+ DSASigner dsa = new DSASigner();
+
+ dsa.init(true, param);
+
+ byte[] message = BigIntegers.asUnsignedByteArray(new BigInteger("968236873715988614170569073515315707566766479517"));
+ BigInteger[] sig = dsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong.", r, sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong.", s, sig[1]);
+ }
+
+ dsa.init(false, pair.getPublic());
+
+ if (!dsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("verification fails");
+ }
+
+ dsa2Test1();
+ dsa2Test2();
+ dsa2Test3();
+ dsa2Test4();
+ }
+
+ private void dsa2Test1()
+ {
+ byte[] seed = Hex.decode("ED8BEE8D1CB89229D2903CBF0E51EE7377F48698");
+
+ DSAParametersGenerator pGen = new DSAParametersGenerator();
+
+ pGen.init(new DSAParameterGenerationParameters(1024, 160, 80, new DSATestSecureRandom(seed)));
+
+ DSAParameters params = pGen.generateParameters();
+
+ DSAValidationParameters pv = params.getValidationParameters();
+
+ if (pv.getCounter() != 5)
+ {
+ fail("counter incorrect");
+ }
+
+ if (!Arrays.areEqual(seed, pv.getSeed()))
+ {
+ fail("seed incorrect");
+ }
+
+ if (!params.getQ().equals(new BigInteger("E950511EAB424B9A19A2AEB4E159B7844C589C4F", 16)))
+ {
+ fail("Q incorrect");
+ }
+
+ if (!params.getP().equals(new BigInteger(
+ "E0A67598CD1B763B" +
+ "C98C8ABB333E5DDA0CD3AA0E5E1FB5BA8A7B4EABC10BA338" +
+ "FAE06DD4B90FDA70D7CF0CB0C638BE3341BEC0AF8A7330A3" +
+ "307DED2299A0EE606DF035177A239C34A912C202AA5F83B9" +
+ "C4A7CF0235B5316BFC6EFB9A248411258B30B839AF172440" +
+ "F32563056CB67A861158DDD90E6A894C72A5BBEF9E286C6B", 16)))
+ {
+ fail("P incorrect");
+ }
+
+ if (!params.getG().equals(new BigInteger(
+ "D29D5121B0423C27" +
+ "69AB21843E5A3240FF19CACC792264E3BB6BE4F78EDD1B15" +
+ "C4DFF7F1D905431F0AB16790E1F773B5CE01C804E509066A" +
+ "9919F5195F4ABC58189FD9FF987389CB5BEDF21B4DAB4F8B" +
+ "76A055FFE2770988FE2EC2DE11AD92219F0B351869AC24DA" +
+ "3D7BA87011A701CE8EE7BFE49486ED4527B7186CA4610A75", 16)))
+ {
+ fail("G incorrect");
+ }
+
+ DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator();
+
+ kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("D0EC4E50BB290A42E9E355C73D8809345DE2E139")), params));
+
+ AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
+
+ if (!pub.getY().equals(new BigInteger(
+ "25282217F5730501" +
+ "DD8DBA3EDFCF349AAFFEC20921128D70FAC44110332201BB" +
+ "A3F10986140CBB97C726938060473C8EC97B4731DB004293" +
+ "B5E730363609DF9780F8D883D8C4D41DED6A2F1E1BBBDC97" +
+ "9E1B9D6D3C940301F4E978D65B19041FCF1E8B518F5C0576" +
+ "C770FE5A7A485D8329EE2914A2DE1B5DA4A6128CEAB70F79", 16)))
+ {
+ fail("Y value incorrect");
+ }
+
+ if (!priv.getX().equals(
+ new BigInteger("D0EC4E50BB290A42E9E355C73D8809345DE2E139", 16)))
+ {
+ fail("X value incorrect");
+ }
+
+ DSASigner signer = new DSASigner();
+
+ signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("349C55648DCF992F3F33E8026CFAC87C1D2BA075"))));
+
+ byte[] msg = Hex.decode("A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+ BigInteger[] sig = signer.generateSignature(msg);
+
+ if (!sig[0].equals(new BigInteger("636155AC9A4633B4665D179F9E4117DF68601F34", 16)))
+ {
+ fail("R value incorrect");
+ }
+
+ if (!sig[1].equals(new BigInteger("6C540B02D9D4852F89DF8CFC99963204F4347704", 16)))
+ {
+ fail("S value incorrect");
+ }
+
+ signer.init(false, kp.getPublic());
+
+ if (!signer.verifySignature(msg, sig[0], sig[1]))
+ {
+ fail("signature not verified");
+ }
+
+ }
+
+ private void dsa2Test2()
+ {
+ byte[] seed = Hex.decode("5AFCC1EFFC079A9CCA6ECA86D6E3CC3B18642D9BE1CC6207C84002A9");
+
+ DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA224Digest());
+
+ pGen.init(new DSAParameterGenerationParameters(2048, 224, 80, new DSATestSecureRandom(seed)));
+
+ DSAParameters params = pGen.generateParameters();
+
+ DSAValidationParameters pv = params.getValidationParameters();
+
+ if (pv.getCounter() != 21)
+ {
+ fail("counter incorrect");
+ }
+
+ if (!Arrays.areEqual(seed, pv.getSeed()))
+ {
+ fail("seed incorrect");
+ }
+
+ if (!params.getQ().equals(new BigInteger("90EAF4D1AF0708B1B612FF35E0A2997EB9E9D263C9CE659528945C0D", 16)))
+ {
+ fail("Q incorrect");
+ }
+
+ if (!params.getP().equals(new BigInteger(
+ "C196BA05AC29E1F9C3C72D56DFFC6154" +
+ "A033F1477AC88EC37F09BE6C5BB95F51C296DD20D1A28A06" +
+ "7CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE4" +
+ "28782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE6" +
+ "19ECACC7E0B51652A8776D02A425567DED36EABD90CA33A1" +
+ "E8D988F0BBB92D02D1D20290113BB562CE1FC856EEB7CDD9" +
+ "2D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BF" +
+ "FAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E" +
+ "5320121496DC65B3930E38047294FF877831A16D5228418D" +
+ "E8AB275D7D75651CEFED65F78AFC3EA7FE4D79B35F62A040" +
+ "2A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83", 16)))
+ {
+ fail("P incorrect");
+ }
+
+ if (!params.getG().equals(new BigInteger(
+ "A59A749A11242C58C894E9E5A91804E8"+
+ "FA0AC64B56288F8D47D51B1EDC4D65444FECA0111D78F35F"+
+ "C9FDD4CB1F1B79A3BA9CBEE83A3F811012503C8117F98E50"+
+ "48B089E387AF6949BF8784EBD9EF45876F2E6A5A495BE64B"+
+ "6E770409494B7FEE1DBB1E4B2BC2A53D4F893D418B715959"+
+ "2E4FFFDF6969E91D770DAEBD0B5CB14C00AD68EC7DC1E574"+
+ "5EA55C706C4A1C5C88964E34D09DEB753AD418C1AD0F4FDF"+
+ "D049A955E5D78491C0B7A2F1575A008CCD727AB376DB6E69"+
+ "5515B05BD412F5B8C2F4C77EE10DA48ABD53F5DD498927EE"+
+ "7B692BBBCDA2FB23A516C5B4533D73980B2A3B60E384ED20"+
+ "0AE21B40D273651AD6060C13D97FD69AA13C5611A51B9085", 16)))
+ {
+ fail("G incorrect");
+ }
+
+ DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator();
+
+ kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("00D0F09ED3E2568F6CADF9224117DA2AEC5A4300E009DE1366023E17")), params));
+
+ AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
+
+ if (!pub.getY().equals(new BigInteger(
+ "70035C9A3B225B258F16741F3941FBF0" +
+ "6F3D056CD7BD864604CBB5EE9DD85304EE8E8E4ABD5E9032" +
+ "11DDF25CE149075510ACE166970AFDC7DF552B7244F342FA" +
+ "02F7A621405B754909D757F97290E1FE5036E904CF593446" +
+ "0C046D95659821E1597ED9F2B1F0E20863A6BBD0CE74DACB" +
+ "A5D8C68A90B29C2157CDEDB82EC12B81EE3068F9BF5F7F34" +
+ "6ECA41ED174CCCD7D154FA4F42F80FFE1BF46AE9D8125DEB" +
+ "5B4BA08A72BDD86596DBEDDC9550FDD650C58F5AE5133509" +
+ "A702F79A31ECB490F7A3C5581631F7C5BE4FF7F9E9F27FA3" +
+ "90E47347AD1183509FED6FCF198BA9A71AB3335B4F38BE8D" +
+ "15496A00B6DC2263E20A5F6B662320A3A1EC033AA61E3B68", 16)))
+ {
+ fail("Y value incorrect");
+ }
+
+ if (!priv.getX().equals(
+ new BigInteger("00D0F09ED3E2568F6CADF9224117DA2AEC5A4300E009DE1366023E17", 16)))
+ {
+ fail("X value incorrect");
+ }
+
+ DSASigner signer = new DSASigner();
+
+ signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("735959CC4463B8B440E407EECA8A473BF6A6D1FE657546F67D401F05"))));
+
+ byte[] msg = Hex.decode("23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+
+ BigInteger[] sig = signer.generateSignature(msg);
+
+ if (!sig[0].equals(new BigInteger("4400138D05F9639CAF54A583CAAF25D2B76D0C3EAD752CE17DBC85FE", 16)))
+ {
+ fail("R value incorrect");
+ }
+
+ if (!sig[1].equals(new BigInteger("874D4F12CB13B61732D398445698CFA9D92381D938AA57EE2C9327B3", 16)))
+ {
+ fail("S value incorrect");
+ }
+
+ signer.init(false, kp.getPublic());
+
+ if (!signer.verifySignature(msg, sig[0], sig[1]))
+ {
+ fail("signature not verified");
+ }
+ }
+
+ private void dsa2Test3()
+ {
+ byte[] seed = Hex.decode("4783081972865EA95D43318AB2EAF9C61A2FC7BBF1B772A09017BDF5A58F4FF0");
+
+ DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA256Digest());
+
+ pGen.init(new DSAParameterGenerationParameters(2048, 256, 80, new DSATestSecureRandom(seed)));
+
+ DSAParameters params = pGen.generateParameters();
+
+ DSAValidationParameters pv = params.getValidationParameters();
+
+ if (pv.getCounter() != 12)
+ {
+ fail("counter incorrect");
+ }
+
+ if (!Arrays.areEqual(seed, pv.getSeed()))
+ {
+ fail("seed incorrect");
+ }
+
+ if (!params.getQ().equals(new BigInteger("C24ED361870B61E0D367F008F99F8A1F75525889C89DB1B673C45AF5867CB467", 16)))
+ {
+ fail("Q incorrect");
+ }
+
+ if (!params.getP().equals(new BigInteger(
+ "F56C2A7D366E3EBDEAA1891FD2A0D099" +
+ "436438A673FED4D75F594959CFFEBCA7BE0FC72E4FE67D91" +
+ "D801CBA0693AC4ED9E411B41D19E2FD1699C4390AD27D94C" +
+ "69C0B143F1DC88932CFE2310C886412047BD9B1C7A67F8A2" +
+ "5909132627F51A0C866877E672E555342BDF9355347DBD43" +
+ "B47156B2C20BAD9D2B071BC2FDCF9757F75C168C5D9FC431" +
+ "31BE162A0756D1BDEC2CA0EB0E3B018A8B38D3EF2487782A" +
+ "EB9FBF99D8B30499C55E4F61E5C7DCEE2A2BB55BD7F75FCD" +
+ "F00E48F2E8356BDB59D86114028F67B8E07B127744778AFF" +
+ "1CF1399A4D679D92FDE7D941C5C85C5D7BFF91BA69F9489D" +
+ "531D1EBFA727CFDA651390F8021719FA9F7216CEB177BD75", 16)))
+ {
+ fail("P incorrect");
+ }
+
+ if (!params.getG().equals(new BigInteger(
+ "8DC6CC814CAE4A1C05A3E186A6FE27EA" +
+ "BA8CDB133FDCE14A963A92E809790CBA096EAA26140550C1" +
+ "29FA2B98C16E84236AA33BF919CD6F587E048C52666576DB" +
+ "6E925C6CBE9B9EC5C16020F9A44C9F1C8F7A8E611C1F6EC2" +
+ "513EA6AA0B8D0F72FED73CA37DF240DB57BBB27431D61869" +
+ "7B9E771B0B301D5DF05955425061A30DC6D33BB6D2A32BD0" +
+ "A75A0A71D2184F506372ABF84A56AEEEA8EB693BF29A6403" +
+ "45FA1298A16E85421B2208D00068A5A42915F82CF0B858C8" +
+ "FA39D43D704B6927E0B2F916304E86FB6A1B487F07D8139E" +
+ "428BB096C6D67A76EC0B8D4EF274B8A2CF556D279AD267CC" +
+ "EF5AF477AFED029F485B5597739F5D0240F67C2D948A6279", 16)))
+ {
+ fail("G incorrect");
+ }
+
+ DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator();
+
+ kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C")), params));
+
+ AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
+
+ if (!pub.getY().equals(new BigInteger(
+ "2828003D7C747199143C370FDD07A286" +
+ "1524514ACC57F63F80C38C2087C6B795B62DE1C224BF8D1D" +
+ "1424E60CE3F5AE3F76C754A2464AF292286D873A7A30B7EA" +
+ "CBBC75AAFDE7191D9157598CDB0B60E0C5AA3F6EBE425500" +
+ "C611957DBF5ED35490714A42811FDCDEB19AF2AB30BEADFF" +
+ "2907931CEE7F3B55532CFFAEB371F84F01347630EB227A41" +
+ "9B1F3F558BC8A509D64A765D8987D493B007C4412C297CAF" +
+ "41566E26FAEE475137EC781A0DC088A26C8804A98C23140E" +
+ "7C936281864B99571EE95C416AA38CEEBB41FDBFF1EB1D1D" +
+ "C97B63CE1355257627C8B0FD840DDB20ED35BE92F08C49AE" +
+ "A5613957D7E5C7A6D5A5834B4CB069E0831753ECF65BA02B", 16)))
+ {
+ fail("Y value incorrect");
+ }
+
+ if (!priv.getX().equals(
+ new BigInteger("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C", 16)))
+ {
+ fail("X value incorrect");
+ }
+
+ DSASigner signer = new DSASigner();
+
+ signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("0CAF2EF547EC49C4F3A6FE6DF4223A174D01F2C115D49A6F73437C29A2A8458C"))));
+
+ byte[] msg = Hex.decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD");
+
+ BigInteger[] sig = signer.generateSignature(msg);
+
+ if (!sig[0].equals(new BigInteger("315C875DCD4850E948B8AC42824E9483A32D5BA5ABE0681B9B9448D444F2BE3C", 16)))
+ {
+ fail("R value incorrect");
+ }
+
+ if (!sig[1].equals(new BigInteger("89718D12E54A8D9ED066E4A55F7ED5A2229CD23B9A3CEE78F83ED6AA61F6BCB9", 16)))
+ {
+ fail("S value incorrect");
+ }
+
+ signer.init(false, kp.getPublic());
+
+ if (!signer.verifySignature(msg, sig[0], sig[1]))
+ {
+ fail("signature not verified");
+ }
+ }
+
+ private void dsa2Test4()
+ {
+ byte[] seed = Hex.decode("193AFCA7C1E77B3C1ECC618C81322E47B8B8B997C9C83515C59CC446C2D9BD47");
+
+ DSAParametersGenerator pGen = new DSAParametersGenerator(new SHA256Digest());
+
+ pGen.init(new DSAParameterGenerationParameters(3072, 256, 80, new DSATestSecureRandom(seed)));
+
+ DSAParameters params = pGen.generateParameters();
+
+ DSAValidationParameters pv = params.getValidationParameters();
+
+ if (pv.getCounter() != 20)
+ {
+ fail("counter incorrect");
+ }
+
+ if (!Arrays.areEqual(seed, pv.getSeed()))
+ {
+ fail("seed incorrect");
+ }
+
+ if (!params.getQ().equals(new BigInteger("CFA0478A54717B08CE64805B76E5B14249A77A4838469DF7F7DC987EFCCFB11D", 16)))
+ {
+ fail("Q incorrect");
+ }
+
+ if (!params.getP().equals(new BigInteger(
+ "90066455B5CFC38F9CAA4A48B4281F292C260FEEF01FD610" +
+ "37E56258A7795A1C7AD46076982CE6BB956936C6AB4DCFE0" +
+ "5E6784586940CA544B9B2140E1EB523F009D20A7E7880E4E" +
+ "5BFA690F1B9004A27811CD9904AF70420EEFD6EA11EF7DA1" +
+ "29F58835FF56B89FAA637BC9AC2EFAAB903402229F491D8D" +
+ "3485261CD068699B6BA58A1DDBBEF6DB51E8FE34E8A78E54" +
+ "2D7BA351C21EA8D8F1D29F5D5D15939487E27F4416B0CA63" +
+ "2C59EFD1B1EB66511A5A0FBF615B766C5862D0BD8A3FE7A0" +
+ "E0DA0FB2FE1FCB19E8F9996A8EA0FCCDE538175238FC8B0E" +
+ "E6F29AF7F642773EBE8CD5402415A01451A840476B2FCEB0" +
+ "E388D30D4B376C37FE401C2A2C2F941DAD179C540C1C8CE0" +
+ "30D460C4D983BE9AB0B20F69144C1AE13F9383EA1C08504F" +
+ "B0BF321503EFE43488310DD8DC77EC5B8349B8BFE97C2C56" +
+ "0EA878DE87C11E3D597F1FEA742D73EEC7F37BE43949EF1A" +
+ "0D15C3F3E3FC0A8335617055AC91328EC22B50FC15B941D3" +
+ "D1624CD88BC25F3E941FDDC6200689581BFEC416B4B2CB73", 16)))
+ {
+ fail("P incorrect");
+ }
+
+ if (!params.getG().equals(new BigInteger(
+ "5E5CBA992E0A680D885EB903AEA78E4A45A469103D448EDE" +
+ "3B7ACCC54D521E37F84A4BDD5B06B0970CC2D2BBB715F7B8" +
+ "2846F9A0C393914C792E6A923E2117AB805276A975AADB52" +
+ "61D91673EA9AAFFEECBFA6183DFCB5D3B7332AA19275AFA1" +
+ "F8EC0B60FB6F66CC23AE4870791D5982AAD1AA9485FD8F4A" +
+ "60126FEB2CF05DB8A7F0F09B3397F3937F2E90B9E5B9C9B6" +
+ "EFEF642BC48351C46FB171B9BFA9EF17A961CE96C7E7A7CC" +
+ "3D3D03DFAD1078BA21DA425198F07D2481622BCE45969D9C" +
+ "4D6063D72AB7A0F08B2F49A7CC6AF335E08C4720E31476B6" +
+ "7299E231F8BD90B39AC3AE3BE0C6B6CACEF8289A2E2873D5" +
+ "8E51E029CAFBD55E6841489AB66B5B4B9BA6E2F784660896" +
+ "AFF387D92844CCB8B69475496DE19DA2E58259B090489AC8" +
+ "E62363CDF82CFD8EF2A427ABCD65750B506F56DDE3B98856" +
+ "7A88126B914D7828E2B63A6D7ED0747EC59E0E0A23CE7D8A" +
+ "74C1D2C2A7AFB6A29799620F00E11C33787F7DED3B30E1A2" +
+ "2D09F1FBDA1ABBBFBF25CAE05A13F812E34563F99410E73B", 16)))
+ {
+ fail("G incorrect");
+ }
+
+ DSAKeyPairGenerator kpGen = new DSAKeyPairGenerator();
+
+ kpGen.init(new DSAKeyGenerationParameters(new FixedSecureRandom(Hex.decode("3ABC1587297CE7B9EA1AD6651CF2BC4D7F92ED25CABC8553F567D1B40EBB8764")), params));
+
+ AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
+
+ DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
+ DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
+
+ if (!pub.getY().equals(new BigInteger(
+ "8B891C8692D3DE875879390F2698B26FBECCA6B075535DCE" +
+ "6B0C862577F9FA0DEF6074E7A7624121224A595896ABD4CD" +
+ "A56B2CEFB942E025D2A4282FFAA98A48CDB47E1A6FCB5CFB" +
+ "393EF35AF9DF913102BB303C2B5C36C3F8FC04ED7B8B69FE" +
+ "FE0CF3E1FC05CFA713B3435B2656E913BA8874AEA9F93600" +
+ "6AEB448BCD005D18EC3562A33D04CF25C8D3D69844343442" +
+ "FA3DB7DE618C5E2DA064573E61E6D5581BFB694A23AC87FD" +
+ "5B52D62E954E1376DB8DDB524FFC0D469DF978792EE44173" +
+ "8E5DB05A7DC43E94C11A2E7A4FBE383071FA36D2A7EC8A93" +
+ "88FE1C4F79888A99D3B6105697C2556B79BB4D7E781CEBB3" +
+ "D4866AD825A5E830846072289FDBC941FA679CA82F5F78B7" +
+ "461B2404DB883D215F4E0676CF5493950AC5591697BFEA8D" +
+ "1EE6EC016B89BA51CAFB5F9C84C989FA117375E94578F28B" +
+ "E0B34CE0545DA46266FD77F62D8F2CEE92AB77012AFEBC11" +
+ "008985A821CD2D978C7E6FE7499D1AAF8DE632C21BB48CA5" +
+ "CBF9F31098FD3FD3854C49A65D9201744AACE540354974F9", 16)))
+ {
+ fail("Y value incorrect");
+ }
+
+ if (!priv.getX().equals(
+ new BigInteger("3ABC1587297CE7B9EA1AD6651CF2BC4D7F92ED25CABC8553F567D1B40EBB8764", 16)))
+ {
+ fail("X value incorrect");
+ }
+
+ DSASigner signer = new DSASigner();
+
+ signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(Hex.decode("A6902C1E6E3943C5628061588A8B007BCCEA91DBF12915483F04B24AB0678BEE"))));
+
+ byte[] msg = Hex.decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD");
+
+ BigInteger[] sig = signer.generateSignature(msg);
+
+ if (!sig[0].equals(new BigInteger("5F184E645A38BE8FB4A6871B6503A9D12924C7ABE04B71410066C2ECA6E3BE3E", 16)))
+ {
+ fail("R value incorrect");
+ }
+
+ if (!sig[1].equals(new BigInteger("91EB0C7BA3D4B9B60B825C3D9F2CADA8A2C9D7723267B033CBCDCF8803DB9C18", 16)))
+ {
+ fail("S value incorrect");
+ }
+
+ signer.init(false, kp.getPublic());
+
+ if (!signer.verifySignature(msg, sig[0], sig[1]))
+ {
+ fail("signature not verified");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DSATest());
+ }
+
+ private class DSATestSecureRandom
+ extends FixedSecureRandom
+ {
+ private boolean first = true;
+
+ public DSATestSecureRandom(byte[] value)
+ {
+ super(value);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ if (first)
+ {
+ super.nextBytes(bytes);
+ first = false;
+ }
+ else
+ {
+ bytes[bytes.length - 1] = 2;
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU4145Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU4145Test.java
new file mode 100644
index 00000000..2d3013d4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DSTU4145Test.java
@@ -0,0 +1,278 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.DSTU4145Signer;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.FixedSecureRandom;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class DSTU4145Test
+ extends SimpleTest
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+ private static final BigInteger ONE = BigInteger.valueOf(1);
+
+ public static void main(String[] args)
+ {
+ runTest(new DSTU4145Test());
+ }
+
+ public String getName()
+ {
+ return "DSTU4145";
+ }
+
+ private void test163()
+ throws Exception
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("01025e40bd97db012b7a1d79de8e12932d247f61c6"));
+
+ byte[] hash = Hex.decode("09c9c44277910c9aaee486883a2eb95b7180166ddf73532eeb76edaef52247ff");
+ for (int i = 0; i < hash.length / 2; i++)
+ {
+ byte tmp = hash[i];
+ hash[i] = hash[hash.length - 1 - i];
+ hash[hash.length - 1 - i] = tmp;
+ }
+
+ BigInteger r = new BigInteger("274ea2c0caa014a0d80a424f59ade7a93068d08a7", 16);
+ BigInteger s = new BigInteger("2100d86957331832b8e8c230f5bd6a332b3615aca", 16);
+
+ ECCurve.F2m curve = new ECCurve.F2m(163, 3, 6, 7, ONE, new BigInteger("5FF6108462A2DC8210AB403925E638A19C1455D21", 16));
+ ECPoint P = curve.createPoint(new BigInteger("72d867f93a93ac27df9ff01affe74885c8c540420", 16), new BigInteger("0224a9c3947852b97c5599d5f4ab81122adc3fd9b", 16));
+ BigInteger n = new BigInteger("400000000000000000002BEC12BE2262D39BCF14D", 16);
+
+ BigInteger d = new BigInteger("183f60fdf7951ff47d67193f8d073790c1c9b5a3e", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, r, s))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void test173()
+ throws Exception
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("0000137449348C1249971759D99C252FFE1E14D8B31F"));
+
+ byte[] hash = Hex.decode("0137187EA862117EF1484289470ECAC802C5A651FDA8");
+ for (int i = 0; i < hash.length / 2; i++)
+ {
+ byte tmp = hash[i];
+ hash[i] = hash[hash.length - 1 - i];
+ hash[hash.length - 1 - i] = tmp;
+ }
+
+ BigInteger r = new BigInteger("13ae89746386709cdbd237cc5ec20ca30004a82ead8", 16);
+ BigInteger s = new BigInteger("3597912cdd093b3e711ccb74a79d3c4ab4c7cccdc60", 16);
+
+ ECCurve.F2m curve = new ECCurve.F2m(173, 1, 2, 10, ZERO, new BigInteger("108576C80499DB2FC16EDDF6853BBB278F6B6FB437D9", 16));
+ ECPoint P = curve.createPoint(new BigInteger("BE6628EC3E67A91A4E470894FBA72B52C515F8AEE9", 16), new BigInteger("D9DEEDF655CF5412313C11CA566CDC71F4DA57DB45C", 16));
+ BigInteger n = new BigInteger("800000000000000000000189B4E67606E3825BB2831", 16);
+
+ BigInteger d = new BigInteger("955CD7E344303D1034E66933DC21C8044D42ADB8", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, r, s))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void test283()
+ throws Exception
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("00000000245383CB3AD41BF30F5F7E8FBA858509B2D5558C92D539A6D994BFA98BC6940E"));
+
+ byte[] hash = Hex.decode("0137187EA862117EF1484289470ECAC802C5A651FDA8");
+ for (int i = 0; i < hash.length / 2; i++)
+ {
+ byte tmp = hash[i];
+ hash[i] = hash[hash.length - 1 - i];
+ hash[hash.length - 1 - i] = tmp;
+ }
+
+ BigInteger r = new BigInteger("12a5edcc38d92208ff23036d75b000c7e4bc0f9af2d40b35f15d6fd15e01234e67781a8", 16);
+ BigInteger s = new BigInteger("2de0775577f75b643cf5afc80d4fe10b21100690f17e2cab7bdc9b50ec87c5727aeb515", 16);
+
+ ECCurve.F2m curve = new ECCurve.F2m(283, 5, 7, 12, ONE, new BigInteger("27B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5", 16));
+ ECPoint P = curve.createPoint(new BigInteger("4D95820ACE761110824CE425C8089129487389B7F0E0A9D043DDC0BB0A4CC9EB25", 16), new BigInteger("954C9C4029B2C62DE35C2B9C2A164984BF1101951E3A68ED03DF234DDE5BB2013152F2", 16));
+ BigInteger n = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307", 16);
+
+ BigInteger d = new BigInteger("B844EEAF15213E4BAD4FB84796D68F2448DB8EB7B4621EC0D51929874892C43E", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, r, s))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void test431()
+ throws Exception
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("0000C4224DBBD800988DBAA39DE838294C345CDA5F5929D1174AA8D9340A5E79D10ACADE6B53CF873E7301A3871C2073AD75AB530457"));
+
+ byte[] hash = Hex.decode("0137187EA862117EF1484289470ECAC802C5A651FDA8");
+ for (int i = 0; i < hash.length / 2; i++)
+ {
+ byte tmp = hash[i];
+ hash[i] = hash[hash.length - 1 - i];
+ hash[hash.length - 1 - i] = tmp;
+ }
+
+ BigInteger r = new BigInteger("1911fefb1f494bebcf8dffdf5276946ff9c9f662192ee18c718db47310a439c784fe07577b16e1edbe16179876e0792a634f1c9c3a2e", 16);
+ BigInteger s = new BigInteger("3852170ee801c2083c52f1ea77b987a5432acecd9c654f064e87bf179e0a397151edbca430082e43bd38a67b55424b5bbc7f2713f620", 16);
+
+ ECCurve.F2m curve = new ECCurve.F2m(431, 1, 3, 5, ONE, new BigInteger("3CE10490F6A708FC26DFE8C3D27C4F94E690134D5BFF988D8D28AAEAEDE975936C66BAC536B18AE2DC312CA493117DAA469C640CAF3", 16));
+ ECPoint P = curve.createPoint(new BigInteger("9548BCDF314CEEEAF099C780FFEFBF93F9FE5B5F55547603C9C8FC1A2774170882B3BE35E892C6D4296B8DEA282EC30FB344272791", 16), new BigInteger("4C6CBD7C62A8EEEFDE17A8B5E196E49A22CE6DE128ABD9FBD81FA4411AD5A38E2A810BEDE09A7C6226BCDCB4A4A5DA37B4725E00AA74", 16));
+ BigInteger n = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA3175458009A8C0A724F02F81AA8A1FCBAF80D90C7A95110504CF", 16);
+
+ BigInteger d = new BigInteger("D0F97354E314191FD773E2404F478C8AEE0FF5109F39E6F37D1FEEC8B2ED1691D84C9882CC729E716A71CC013F66CAC60E29E22C", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, r, s))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void testTruncation()
+ {
+ SecureRandom random = new FixedSecureRandom(Hex.decode("0000C4224DBBD800988DBAA39DE838294C345CDA5F5929D1174AA8D9340A5E79D10ACADE6B53CF873E7301A3871C2073AD75AB530457"));
+
+ // use extra long "hash" with set bits...
+ byte[] hash = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+ ECCurve.F2m curve = new ECCurve.F2m(173, 1, 2, 10, ZERO, new BigInteger("108576C80499DB2FC16EDDF6853BBB278F6B6FB437D9", 16));
+ ECPoint P = curve.createPoint(new BigInteger("BE6628EC3E67A91A4E470894FBA72B52C515F8AEE9", 16), new BigInteger("D9DEEDF655CF5412313C11CA566CDC71F4DA57DB45C", 16));
+ BigInteger n = new BigInteger("800000000000000000000189B4E67606E3825BB2831", 16);
+
+ BigInteger d = new BigInteger("955CD7E344303D1034E66933DC21C8044D42ADB8", 16);
+ ECPoint Q = P.multiply(d).negate();
+
+ ECDomainParameters domain = new ECDomainParameters(curve, P, n);
+ CipherParameters privKey = new ParametersWithRandom(new ECPrivateKeyParameters(d, domain), random);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(Q, domain);
+
+ DSTU4145Signer dstuSigner = new DSTU4145Signer();
+ dstuSigner.init(true, privKey);
+ BigInteger[] rs = dstuSigner.generateSignature(hash);
+
+ BigInteger r = new BigInteger("6bb5c0cb82e5067485458ebfe81025f03b687c63a27", 16);
+ BigInteger s = new BigInteger("34d6b1868969b86ecf934167c8fe352c63d1074bd", 16);
+
+ if (rs[0].compareTo(r) != 0)
+ {
+ fail("r component wrong");
+ }
+
+ if (rs[1].compareTo(s) != 0)
+ {
+ fail("s component wrong");
+ }
+
+ dstuSigner.init(false, pubKey);
+ if (!dstuSigner.verifySignature(hash, rs[0], rs[1]))
+ {
+ fail("verification fails");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ test163();
+ test173();
+ test283();
+ test431();
+ testTruncation();
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DeterministicDSATest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DeterministicDSATest.java
new file mode 100644
index 00000000..c47cf8e8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DeterministicDSATest.java
@@ -0,0 +1,513 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA224Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+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.signers.DSASigner;
+import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Tests are taken from RFC 6979 - "Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)"
+ */
+public class DeterministicDSATest
+ extends SimpleTest
+{
+
+ public static final byte[] SAMPLE = Hex.decode("73616d706c65"); // "sample"
+ public static final byte[] TEST = Hex.decode("74657374"); // "test"
+
+ // test vectors from appendix in RFC 6979
+ private void testHMacDeterministic()
+ {
+ DSAParameters dsaParameters = new DSAParameters(
+ new BigInteger("86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447" +
+ "E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED88" +
+ "73ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C" +
+ "881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779", 16),
+ new BigInteger("996F967F6C8E388D9E28D01E205FBA957A5698B1", 16),
+ new BigInteger("07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D" +
+ "89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD" +
+ "87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA4" +
+ "17BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD", 16));
+
+ DSAPrivateKeyParameters privKey = new DSAPrivateKeyParameters(new BigInteger("411602CB19A6CCC34494D79D98EF1E7ED5AF25F7", 16), dsaParameters);
+
+ doTestHMACDetDSASample(new SHA1Digest(), privKey, new BigInteger("2E1A0C2562B2912CAAF89186FB0F42001585DA55", 16), new BigInteger("29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5", 16));
+ doTestHMACDetDSASample(new SHA224Digest(), privKey, new BigInteger("4BC3B686AEA70145856814A6F1BB53346F02101E", 16), new BigInteger("410697B92295D994D21EDD2F4ADA85566F6F94C1", 16));
+ doTestHMACDetDSASample(new SHA256Digest(), privKey, new BigInteger("81F2F5850BE5BC123C43F71A3033E9384611C545", 16), new BigInteger("4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89", 16));
+ doTestHMACDetDSASample(new SHA384Digest(), privKey, new BigInteger("07F2108557EE0E3921BC1774F1CA9B410B4CE65A", 16), new BigInteger("54DF70456C86FAC10FAB47C1949AB83F2C6F7595", 16));
+ doTestHMACDetDSASample(new SHA512Digest(), privKey, new BigInteger("16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B", 16), new BigInteger("02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C", 16));
+
+ doTestHMACDetDSATest(new SHA1Digest(), privKey, new BigInteger("42AB2052FD43E123F0607F115052A67DCD9C5C77", 16), new BigInteger("183916B0230D45B9931491D4C6B0BD2FB4AAF088", 16));
+ doTestHMACDetDSATest(new SHA224Digest(), privKey, new BigInteger("6868E9964E36C1689F6037F91F28D5F2C30610F2", 16), new BigInteger("49CEC3ACDC83018C5BD2674ECAAD35B8CD22940F", 16));
+ doTestHMACDetDSATest(new SHA256Digest(), privKey, new BigInteger("22518C127299B0F6FDC9872B282B9E70D0790812", 16), new BigInteger("6837EC18F150D55DE95B5E29BE7AF5D01E4FE160", 16));
+ doTestHMACDetDSATest(new SHA384Digest(), privKey, new BigInteger("854CF929B58D73C3CBFDC421E8D5430CD6DB5E66", 16), new BigInteger("91D0E0F53E22F898D158380676A871A157CDA622", 16));
+ doTestHMACDetDSATest(new SHA512Digest(), privKey, new BigInteger("8EA47E475BA8AC6F2D821DA3BD212D11A3DEB9A0", 16), new BigInteger("7C670C7AD72B6C050C109E1790008097125433E8", 16));
+
+ dsaParameters = new DSAParameters(
+ new BigInteger("9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" +
+ "C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" +
+ "FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" +
+ "B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" +
+ "35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" +
+ "F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" +
+ "92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" +
+ "3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B", 16),
+ new BigInteger("F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F", 16),
+ new BigInteger("5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" +
+ "D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" +
+ "6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" +
+ "085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" +
+ "AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" +
+ "3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" +
+ "BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" +
+ "DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", 16));
+
+ privKey = new DSAPrivateKeyParameters(new BigInteger("69C7548C21D0DFEA6B9A51C9EAD4E27C33D3B3F180316E5BCAB92C933F0E4DBC", 16), dsaParameters);
+
+ doTestHMACDetDSASample(new SHA1Digest(), privKey, new BigInteger("3A1B2DBD7489D6ED7E608FD036C83AF396E290DBD602408E8677DAABD6E7445A", 16), new BigInteger("D26FCBA19FA3E3058FFC02CA1596CDBB6E0D20CB37B06054F7E36DED0CDBBCCF", 16));
+ doTestHMACDetDSASample(new SHA224Digest(), privKey, new BigInteger("DC9F4DEADA8D8FF588E98FED0AB690FFCE858DC8C79376450EB6B76C24537E2C", 16), new BigInteger("A65A9C3BC7BABE286B195D5DA68616DA8D47FA0097F36DD19F517327DC848CEC", 16));
+ doTestHMACDetDSASample(new SHA256Digest(), privKey, new BigInteger("EACE8BDBBE353C432A795D9EC556C6D021F7A03F42C36E9BC87E4AC7932CC809", 16), new BigInteger("7081E175455F9247B812B74583E9E94F9EA79BD640DC962533B0680793A38D53", 16));
+ doTestHMACDetDSASample(new SHA384Digest(), privKey, new BigInteger("B2DA945E91858834FD9BF616EBAC151EDBC4B45D27D0DD4A7F6A22739F45C00B", 16), new BigInteger("19048B63D9FD6BCA1D9BAE3664E1BCB97F7276C306130969F63F38FA8319021B", 16));
+ doTestHMACDetDSASample(new SHA512Digest(), privKey, new BigInteger("2016ED092DC5FB669B8EFB3D1F31A91EECB199879BE0CF78F02BA062CB4C942E", 16), new BigInteger("D0C76F84B5F091E141572A639A4FB8C230807EEA7D55C8A154A224400AFF2351", 16));
+
+ doTestHMACDetDSATest(new SHA1Digest(), privKey, new BigInteger("C18270A93CFC6063F57A4DFA86024F700D980E4CF4E2CB65A504397273D98EA0", 16), new BigInteger("414F22E5F31A8B6D33295C7539C1C1BA3A6160D7D68D50AC0D3A5BEAC2884FAA", 16));
+ doTestHMACDetDSATest(new SHA224Digest(), privKey, new BigInteger("272ABA31572F6CC55E30BF616B7A265312018DD325BE031BE0CC82AA17870EA3", 16), new BigInteger("E9CC286A52CCE201586722D36D1E917EB96A4EBDB47932F9576AC645B3A60806", 16));
+ doTestHMACDetDSATest(new SHA256Digest(), privKey, new BigInteger("8190012A1969F9957D56FCCAAD223186F423398D58EF5B3CEFD5A4146A4476F0", 16), new BigInteger("7452A53F7075D417B4B013B278D1BB8BBD21863F5E7B1CEE679CF2188E1AB19E", 16));
+ doTestHMACDetDSATest(new SHA384Digest(), privKey, new BigInteger("239E66DDBE8F8C230A3D071D601B6FFBDFB5901F94D444C6AF56F732BEB954BE", 16), new BigInteger("6BD737513D5E72FE85D1C750E0F73921FE299B945AAD1C802F15C26A43D34961", 16));
+ doTestHMACDetDSATest(new SHA512Digest(), privKey, new BigInteger("89EC4BB1400ECCFF8E7D9AA515CD1DE7803F2DAFF09693EE7FD1353E90A68307", 16), new BigInteger("C9F0BDABCC0D880BB137A994CC7F3980CE91CC10FAF529FC46565B15CEA854E1", 16));
+ }
+
+ private void doTestHMACDetDSASample(Digest digest, DSAPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+ {
+ doTestHMACDetECDSA(new DSASigner(new HMacDSAKCalculator(digest)), digest, SAMPLE, privKey, r, s);
+ }
+
+ private void doTestHMACDetDSATest(Digest digest, DSAPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+ {
+ doTestHMACDetECDSA(new DSASigner(new HMacDSAKCalculator(digest)), digest, TEST, privKey, r, s);
+ }
+
+ // test vectors from appendix in RFC 6979
+ private void testECHMacDeterministic()
+ {
+ X9ECParameters x9ECParameters = NISTNamedCurves.getByName("P-192");
+ ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("98C6BD12B23EAF5E2A2045132086BE3EB8EBD62ABF6698FF", 16), new BigInteger("57A22B07DEA9530F8DE9471B1DC6624472E8E2844BC25B64", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("A1F00DAD97AEEC91C95585F36200C65F3C01812AA60378F5", 16), new BigInteger("E07EC1304C7C6C9DEBBE980B9692668F81D4DE7922A0F97A", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55", 16), new BigInteger("CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("DA63BF0B9ABCF948FBB1E9167F136145F7A20426DCC287D5", 16), new BigInteger("C3AA2C960972BD7A2003A57E1C4C77F0578F8AE95E31EC5E", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("4D60C5AB1996BD848343B31C00850205E2EA6922DAC2E4B8", 16), new BigInteger("3F6E837448F027A1BF4B34E796E32A811CBB4050908D8F67", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("0F2141A0EBBC44D2E1AF90A50EBCFCE5E197B3B7D4DE036D", 16), new BigInteger("EB18BC9E1F3D7387500CB99CF5F7C157070A8961E38700B7", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("6945A1C1D1B2206B8145548F633BB61CEF04891BAF26ED34", 16), new BigInteger("B7FB7FDFC339C0B9BD61A9F5A8EAF9BE58FC5CBA2CB15293", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE", 16), new BigInteger("5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("B234B60B4DB75A733E19280A7A6034BD6B1EE88AF5332367", 16), new BigInteger("7994090B2D59BB782BE57E74A44C9A1C700413F8ABEFE77A", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("FE4F4AE86A58B6507946715934FE2D8FF9D95B6B098FE739", 16), new BigInteger("74CF5605C98FBA0E1EF34D4B5A1577A7DCF59457CAE52290", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("P-224");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("22226F9D40A96E19C4A301CE5B74B115303C0F3A4FD30FC257FB57AC", 16), new BigInteger("66D1CDD83E3AF75605DD6E2FEFF196D30AA7ED7A2EDF7AF475403D69", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E", 16), new BigInteger("A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("61AA3DA010E8E8406C656BC477A7A7189895E7E840CDFE8FF42307BA", 16), new BigInteger("BC814050DAB5D23770879494F9E0A680DC1AF7161991BDE692B10101", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("0B115E5E36F0F9EC81F1325A5952878D745E19D7BB3EABFABA77E953", 16), new BigInteger("830F34CCDFE826CCFDC81EB4129772E20E122348A2BBD889A1B1AF1D", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("074BD1D979D5F32BF958DDC61E4FB4872ADCAFEB2256497CDAC30397", 16), new BigInteger("A4CECA196C3D5A1FF31027B33185DC8EE43F288B21AB342E5D8EB084", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("DEAA646EC2AF2EA8AD53ED66B2E2DDAA49A12EFD8356561451F3E21C", 16), new BigInteger("95987796F6CF2062AB8135271DE56AE55366C045F6D9593F53787BD2", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019", 16), new BigInteger("902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("AD04DDE87B84747A243A631EA47A1BA6D1FAA059149AD2440DE6FBA6", 16), new BigInteger("178D49B1AE90E3D8B629BE3DB5683915F4E8C99FDF6E666CF37ADCFD", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("389B92682E399B26518A95506B52C03BC9379A9DADF3391A21FB0EA4", 16), new BigInteger("414A718ED3249FF6DBC5B50C27F71F01F070944DA22AB1F78F559AAB", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("049F050477C5ADD858CAC56208394B5A55BAEBBE887FDF765047C17C", 16), new BigInteger("077EB13E7005929CEFA3CD0403C7CDCC077ADF4E44F3C41B2F60ECFF", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("P-256");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("61340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D32", 16), new BigInteger("6D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("53B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3F", 16), new BigInteger("B9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716", 16), new BigInteger("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("0EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF7719", 16), new BigInteger("4861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("8496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F00", 16), new BigInteger("2362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("0CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A89", 16), new BigInteger("01B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692", 16), new BigInteger("C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367", 16), new BigInteger("019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("83910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB6", 16), new BigInteger("8DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A04", 16), new BigInteger("39AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("P-384");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D8" +
+ "96D5724E4C70A825F872C9EA60D2EDF5", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("EC748D839243D6FBEF4FC5C4859A7DFFD7F3ABDDF72014540C16D73309834FA3" +
+ "7B9BA002899F6FDA3A4A9386790D4EB2", 16),
+ new BigInteger("A3BCFA947BEEF4732BF247AC17F71676CB31A847B9FF0CBC9C9ED4C1A5B3FACF" +
+ "26F49CA031D4857570CCB5CA4424A443", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("42356E76B55A6D9B4631C865445DBE54E056D3B3431766D0509244793C3F9366" +
+ "450F76EE3DE43F5A125333A6BE060122", 16),
+ new BigInteger("9DA0C81787064021E78DF658F2FBB0B042BF304665DB721F077A4298B095E483" +
+ "4C082C03D83028EFBF93A3C23940CA8D", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("21B13D1E013C7FA1392D03C5F99AF8B30C570C6F98D4EA8E354B63A21D3DAA33" +
+ "BDE1E888E63355D92FA2B3C36D8FB2CD", 16),
+ new BigInteger("F3AA443FB107745BF4BD77CB3891674632068A10CA67E3D45DB2266FA7D1FEEB" +
+ "EFDC63ECCD1AC42EC0CB8668A4FA0AB0", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C" +
+ "81A648152E44ACF96E36DD1E80FABE46", 16),
+ new BigInteger("99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94F" +
+ "A329C145786E679E7B82C71A38628AC8", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("ED0959D5880AB2D869AE7F6C2915C6D60F96507F9CB3E047C0046861DA4A799C" +
+ "FE30F35CC900056D7C99CD7882433709", 16),
+ new BigInteger("512C8CCEEE3890A84058CE1E22DBC2198F42323CE8ACA9135329F03C068E5112" +
+ "DC7CC3EF3446DEFCEB01A45C2667FDD5", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("4BC35D3A50EF4E30576F58CD96CE6BF638025EE624004A1F7789A8B8E43D0678" +
+ "ACD9D29876DAF46638645F7F404B11C7", 16),
+ new BigInteger("D5A6326C494ED3FF614703878961C0FDE7B2C278F9A65FD8C4B7186201A29916" +
+ "95BA1C84541327E966FA7B50F7382282", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("E8C9D0B6EA72A0E7837FEA1D14A1A9557F29FAA45D3E7EE888FC5BF954B5E624" +
+ "64A9A817C47FF78B8C11066B24080E72", 16),
+ new BigInteger("07041D4A7A0379AC7232FF72E6F77B6DDB8F09B16CCE0EC3286B2BD43FA8C614" +
+ "1C53EA5ABEF0D8231077A04540A96B66", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("6D6DEFAC9AB64DABAFE36C6BF510352A4CC27001263638E5B16D9BB51D451559" +
+ "F918EEDAF2293BE5B475CC8F0188636B", 16),
+ new BigInteger("2D46F3BECBCC523D5F1A1256BF0C9B024D879BA9E838144C8BA6BAEB4B53B47D" +
+ "51AB373F9845C0514EEFB14024787265", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB" +
+ "0542A7F0812998DA8F1DD3CA3CF023DB", 16),
+ new BigInteger("DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E0" +
+ "6A739F040649A667BF3B828246BAA5A5", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("A0D5D090C9980FAF3C2CE57B7AE951D31977DD11C775D314AF55F76C676447D0" +
+ "6FB6495CD21B4B6E340FC236584FB277", 16),
+ new BigInteger("976984E59B4C77B0E8E4460DCA3D9F20E07B9BB1F63BEEFAF576F6B2E8B22463" +
+ "4A2092CD3792E0159AD9CEE37659C736", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("P-521");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75C" +
+ "AA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83" +
+ "538", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("0343B6EC45728975EA5CBA6659BBB6062A5FF89EEA58BE3C80B619F322C87910" +
+ "FE092F7D45BB0F8EEE01ED3F20BABEC079D202AE677B243AB40B5431D497C55D" +
+ "75D", 16),
+ new BigInteger("0E7B0E675A9B24413D448B8CC119D2BF7B2D2DF032741C096634D6D65D0DBE3D" +
+ "5694625FB9E8104D3B842C1B0E2D0B98BEA19341E8676AEF66AE4EBA3D5475D5" +
+ "D16", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("1776331CFCDF927D666E032E00CF776187BC9FDD8E69D0DABB4109FFE1B5E2A3" +
+ "0715F4CC923A4A5E94D2503E9ACFED92857B7F31D7152E0F8C00C15FF3D87E2E" +
+ "D2E", 16),
+ new BigInteger("050CB5265417FE2320BBB5A122B8E1A32BD699089851128E360E620A30C7E17B" +
+ "A41A666AF126CE100E5799B153B60528D5300D08489CA9178FB610A2006C254B" +
+ "41F", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("1511BB4D675114FE266FC4372B87682BAECC01D3CC62CF2303C92B3526012659" +
+ "D16876E25C7C1E57648F23B73564D67F61C6F14D527D54972810421E7D87589E" +
+ "1A7", 16),
+ new BigInteger("04A171143A83163D6DF460AAF61522695F207A58B95C0644D87E52AA1A347916" +
+ "E4F7A72930B1BC06DBE22CE3F58264AFD23704CBB63B29B931F7DE6C9D949A7E" +
+ "CFC", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("1EA842A0E17D2DE4F92C15315C63DDF72685C18195C2BB95E572B9C5136CA4B4" +
+ "B576AD712A52BE9730627D16054BA40CC0B8D3FF035B12AE75168397F5D50C67" +
+ "451", 16),
+ new BigInteger("1F21A3CEE066E1961025FB048BD5FE2B7924D0CD797BABE0A83B66F1E35EEAF5" +
+ "FDE143FA85DC394A7DEE766523393784484BDF3E00114A1C857CDE1AA203DB65" +
+ "D61", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F1" +
+ "74E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E37" +
+ "7FA", 16),
+ new BigInteger("0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF2" +
+ "82623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A" +
+ "67A", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("13BAD9F29ABE20DE37EBEB823C252CA0F63361284015A3BF430A46AAA80B87B0" +
+ "693F0694BD88AFE4E661FC33B094CD3B7963BED5A727ED8BD6A3A202ABE009D0" +
+ "367", 16),
+ new BigInteger("1E9BB81FF7944CA409AD138DBBEE228E1AFCC0C890FC78EC8604639CB0DBDC90" +
+ "F717A99EAD9D272855D00162EE9527567DD6A92CBD629805C0445282BBC91679" +
+ "7FF", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("1C7ED902E123E6815546065A2C4AF977B22AA8EADDB68B2C1110E7EA44D42086" +
+ "BFE4A34B67DDC0E17E96536E358219B23A706C6A6E16BA77B65E1C595D43CAE1" +
+ "7FB", 16),
+ new BigInteger("177336676304FCB343CE028B38E7B4FBA76C1C1B277DA18CAD2A8478B2A9A9F5" +
+ "BEC0F3BA04F35DB3E4263569EC6AADE8C92746E4C82F8299AE1B8F1739F8FD51" +
+ "9A4", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("00E871C4A14F993C6C7369501900C4BC1E9C7B0B4BA44E04868B30B41D807104" +
+ "2EB28C4C250411D0CE08CD197E4188EA4876F279F90B3D8D74A3C76E6F1E4656" +
+ "AA8", 16),
+ new BigInteger("0CD52DBAA33B063C3A6CD8058A1FB0A46A4754B034FCC644766CA14DA8CA5CA9" +
+ "FDE00E88C1AD60CCBA759025299079D7A427EC3CC5B619BFBC828E7769BCD694" +
+ "E86", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("14BEE21A18B6D8B3C93FAB08D43E739707953244FDBE924FA926D76669E7AC8C" +
+ "89DF62ED8975C2D8397A65A49DCC09F6B0AC62272741924D479354D74FF60755" +
+ "78C", 16),
+ new BigInteger("133330865C067A0EAF72362A65E2D7BC4E461E8C8995C3B6226A21BD1AA78F0E" +
+ "D94FE536A0DCA35534F0CD1510C41525D163FE9D74D134881E35141ED5E8E95B" +
+ "979", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10" +
+ "CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47E" +
+ "E6D", 16),
+ new BigInteger("1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78" +
+ "A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4D" +
+ "CE3", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-163");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("35318FC447D48D7E6BC93B48617DDDEDF26AA658F", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("153FEBD179A69B6122DEBF5BC61EB947B24C93526", 16), new BigInteger("37AC9C670F8CF18045049BAE7DD35553545C19E49", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("0A379E69C44F9C16EA3215EA39EB1A9B5D58CC955", 16), new BigInteger("04BAFF5308DA2A7FE2C1742769265AD3ED1D24E74", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("134E00F78FC1CB9501675D91C401DE20DDF228CDC", 16), new BigInteger("373273AEC6C36CB7BAFBB1903A5F5EA6A1D50B624", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("29430B935AF8E77519B0CA4F6903B0B82E6A21A66", 16), new BigInteger("1EA1415306E9353FA5AA54BC7C2581DFBB888440D", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("0B2F177A99F9DF2D51CCAF55F015F326E4B65E7A0", 16), new BigInteger("0DF1FB4487E9B120C5E970EFE48F55E406306C3A1", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("256D4079C6C7169B8BC92529D701776A269D56308", 16), new BigInteger("341D3FFEC9F1EB6A6ACBE88E3C86A1C8FDEB8B8E1", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("28ECC6F1272CE80EA59DCF32F7AC2D861BA803393", 16), new BigInteger("0AD4AE2C06E60183C1567D2B82F19421FE3053CE2", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("227DF377B3FA50F90C1CB3CDCBBDBA552C1D35104", 16), new BigInteger("1F7BEAD92583FE920D353F368C1960D0E88B46A56", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("11811DAFEEA441845B6118A0DFEE8A0061231337D", 16), new BigInteger("36258301865EE48C5C6F91D63F62695002AB55B57", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("3B6BB95CA823BE2ED8E3972FF516EB8972D765571", 16), new BigInteger("13DC6F420628969DF900C3FCC48220B38BE24A541", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-233");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("07ADC13DD5BF34D1DDEEB50B2CE23B5F5E6D18067306D60C5F6FF11E5D3", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("015CC6FD78BB06E0878E71465515EA5A21A2C18E6FC77B4B158DBEB3944", 16), new BigInteger("0822A4A6C2EB2DF213A5E90BF40377956365EE8C4B4A5A4E2EB9270CB6A", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("05D9920B53471148E10502AB49AB7A3F11084820A074FD89883CF51BC1A", 16), new BigInteger("04D3938900C0A9AAA7080D1DFEB56CFB0FADABE4214536C7ED5117ED13A", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("0A797F3B8AEFCE7456202DF1E46CCC291EA5A49DA3D4BDDA9A4B62D5E0D", 16), new BigInteger("01F6F81DA55C22DA4152134C661588F4BD6F82FDBAF0C5877096B070DC2", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("015E85A8D46225DD7E314A1C4289731FC14DECE949349FE535D11043B85", 16), new BigInteger("03F189D37F50493EFD5111A129443A662AB3C6B289129AD8C0CAC85119C", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("03B62A4BF783919098B1E42F496E65F7621F01D1D466C46940F0F132A95", 16), new BigInteger("0F4BE031C6E5239E7DAA014CBBF1ED19425E49DAEB426EC9DF4C28A2E30", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("02F1FEDC57BE203E4C8C6B8C1CEB35E13C1FCD956AB41E3BD4C8A6EFB1F", 16), new BigInteger("05738EC8A8EDEA8E435EE7266AD3EDE1EEFC2CEBE2BE1D614008D5D2951", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("0CCE175124D3586BA7486F7146894C65C2A4A5A1904658E5C7F9DF5FA5D", 16), new BigInteger("08804B456D847ACE5CA86D97BF79FD6335E5B17F6C0D964B5D0036C867E", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("035C3D6DFEEA1CFB29B93BE3FDB91A7B130951770C2690C16833A159677", 16), new BigInteger("0600F7301D12AB376B56D4459774159ADB51F97E282FF384406AFD53A02", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("061602FC8068BFD5FB86027B97455D200EC603057446CCE4D76DB8EF42C", 16), new BigInteger("03396DD0D59C067BB999B422D9883736CF9311DFD6951F91033BD03CA8D", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("07E12CB60FDD614958E8E34B3C12DDFF35D85A9C5800E31EA2CC2EF63B1", 16), new BigInteger("0E8970FD99D836F3CC1C807A2C58760DE6EDAA23705A82B9CB1CE93FECC", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-283");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("14510D4BC44F2D26F4553942C98073C1BD35545CEABB5CC138853C5158D2729EA408836", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("201E18D48C6DB3D5D097C4DCE1E25587E1501FC3CF47BDB5B4289D79E273D6A9" +
+ "ACB8285", 16), new BigInteger("151AE05712B024CE617358260774C8CA8B0E7A7E72EF8229BF2ACE7609560CB3" +
+ "0322C4F", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("143E878DDFD4DF40D97B8CD638B3C4706501C2201CF7108F2FB91478C11D6947" +
+ "3246925", 16), new BigInteger("0CBF1B9717FEEA3AABB09D9654110144267098E0E1E8D0289A6211BE0EEDFDD8" +
+ "6A3DB79", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("29FD82497FB3E5CEF65579272138DE59E2B666B8689466572B3B69A172CEE83B" +
+ "E145659", 16), new BigInteger("05A89D9166B40795AF0FE5958201B9C0523E500013CA12B4840EA2BC53F25F9B" +
+ "3CE87C0", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("2F00689C1BFCD2A8C7A41E0DE55AE182E6463A152828EF89FE3525139B660329" +
+ "4E69353", 16), new BigInteger("1744514FE0A37447250C8A329EAAADA81572226CABA16F39270EE5DD03F27B1F" +
+ "665EB5D", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("0DA43A9ADFAA6AD767998A054C6A8F1CF77A562924628D73C62761847AD8286E" +
+ "0D91B47", 16), new BigInteger("1D118733AE2C88357827CAFC6F68ABC25C80C640532925E95CFE66D40F8792F3" +
+ "AC44C42", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("05A408133919F2CDCDBE5E4C14FBC706C1F71BADAFEF41F5DE4EC27272FC1CA9" +
+ "366FBB2", 16), new BigInteger("012966272872C097FEA7BCE64FAB1A81982A773E26F6E4EF7C99969846E67CA9" +
+ "CBE1692", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("08F3824E40C16FF1DDA8DC992776D26F4A5981AB5092956C4FDBB4F1AE0A711E" +
+ "EAA10E5", 16), new BigInteger("0A64B91EFADB213E11483FB61C73E3EF63D3B44EEFC56EA401B99DCC60CC28E9" +
+ "9F0F1FA", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("3597B406F5329D11A79E887847E5EC60861CCBB19EC61F252DB7BD549C699951" +
+ "C182796", 16), new BigInteger("0A6A100B997BC622D91701D9F5C6F6D3815517E577622DA69D3A0E8917C1CBE6" +
+ "3ACD345", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("1BB490926E5A1FDC7C5AA86D0835F9B994EDA315CA408002AF54A298728D422E" +
+ "BF59E4C", 16), new BigInteger("36C682CFC9E2C89A782BFD3A191609D1F0C1910D5FD6981442070393159D65FB" +
+ "CC0A8BA", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("19944AA68F9778C2E3D6E240947613E6DA60EFCE9B9B2C063FF5466D72745B5A" +
+ "0B25BA2", 16), new BigInteger("03F1567B3C5B02DF15C874F0EE22850824693D5ADC4663BAA19E384E550B1DD4" +
+ "1F31EE6", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-409");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("0494994CC325B08E7B4CE038BD9436F90B5E59A2C13C3140CD3AE07C04A01FC489F572CE0569A6DB7B8060393DE76330C624177", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("0D8783188E1A540E2022D389E1D35B32F56F8C2BB5636B8ABF7718806B27A713" +
+ "EBAE37F63ECD4B61445CEF5801B62594EF3E982", 16), new BigInteger("03A6B4A80E204DB0DE12E7415C13C9EC091C52935658316B4A0C591216A38791" +
+ "54BEB1712560E346E7EF26517707435B55C3141", 16));
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("0EE4F39ACC2E03CE96C3D9FCBAFA5C22C89053662F8D4117752A9B10F09ADFDA" +
+ "59DB061E247FE5321D6B170EE758ACE1BE4D157", 16), new BigInteger("00A2B83265B456A430A8BF27DCC8A9488B3F126C10F0D6D64BF7B8A218FAAF20" +
+ "E51A295A3AE78F205E5A4A6AE224C3639F1BB34", 16));
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("02D8B1B31E33E74D7EB46C30FDE5AD2CA04EC8FE08FBA0E73BA5E568953AC5EA" +
+ "307C072942238DFC07F4A4D7C7C6A9F86436D17", 16), new BigInteger("079F7D471E6CB73234AF7F7C381D2CE15DE35BAF8BB68393B73235B3A26EC2DF" +
+ "4842CE433FB492D6E074E604D4870024D42189A", 16));
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("07BC638B7E7CE6FEE5E9C64A0F966D722D01BB4BC3F3A35F30D4CDDA92DFC5F7" +
+ "F0B4BBFE8065D9AD452FD77A1914BE3A2440C18", 16), new BigInteger("06D904429850521B28A32CBF55C7C0FDF35DC4E0BDA2552C7BF68A171E970E67" +
+ "88ACC0B9521EACB4796E057C70DD9B95FED5BFB", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("05D178DECAFD2D02A3DA0D8BA1C4C1D95EE083C760DF782193A9F7B4A8BE6FC5" +
+ "C21FD60613BCA65C063A61226E050A680B3ABD4", 16), new BigInteger("013B7581E98F6A63FBBCB3E49BCDA60F816DB230B888506D105DC229600497C3" +
+ "B46588C784BE3AA9343BEF82F7C9C80AEB63C3B", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("049F54E7C10D2732B4638473053782C6919218BBEFCEC8B51640FC193E832291" +
+ "F05FA12371E9B448417B3290193F08EE9319195", 16), new BigInteger("0499E267DEC84E02F6F108B10E82172C414F15B1B7364BE8BFD66ADC0C5DE23F" +
+ "EE3DF0D811134C25AFE0E05A6672F98889F28F1", 16));
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("0B1527FFAA7DD7C7E46B628587A5BEC0539A2D04D3CF27C54841C2544E1BBDB4" +
+ "2FDBDAAF8671A4CA86DFD619B1E3732D7BB56F2", 16), new BigInteger("0442C68C044868DF4832C807F1EDDEBF7F5052A64B826FD03451440794063F52" +
+ "B022DF304F47403D4069234CA9EB4C964B37C02", 16));
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("0BB27755B991D6D31757BCBF68CB01225A38E1CFA20F775E861055DD108ED7EA" +
+ "455E4B96B2F6F7CD6C6EC2B3C70C3EDDEB9743B", 16), new BigInteger("0C5BE90980E7F444B5F7A12C9E9AC7A04CA81412822DD5AD1BE7C45D5032555E" +
+ "A070864245CF69266871FEB8CD1B7EDC30EF6D5", 16));
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("04EFEB7098772187907C87B33E0FBBA4584226C50C11E98CA7AAC6986F8D3BE0" +
+ "44E5B52D201A410B852536527724CA5F8CE6549", 16), new BigInteger("09574102FEB3EF87E6D66B94119F5A6062950FF4F902EA1E6BD9E2037F33FF99" +
+ "1E31F5956C23AFE48FCDC557FD6F088C7C9B2B3", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("07E0249C68536AE2AEC2EC30090340DA49E6DC9E9EEC8F85E5AABFB234B6DA7D" +
+ "2E9524028CF821F21C6019770474CC40B01FAF6", 16), new BigInteger("08125B5A03FB44AE81EA46D446130C2A415ECCA265910CA69D55F2453E16CD7B" +
+ "2DFA4E28C50FA8137F9C0C6CEE4CD37ABCCF6D8", 16));
+
+ x9ECParameters = NISTNamedCurves.getByName("B-571");
+ ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
+
+ privKey = new ECPrivateKeyParameters(new BigInteger("028A04857F24C1C082DF0D909C0E72F453F2E2340CCB071F0E389BCA2575DA19" +
+ "124198C57174929AD26E348CF63F78D28021EF5A9BF2D5CBEAF6B7CCB6C4DA82" +
+ "4DD5C82CFB24E11", 16), ecDomainParameters);
+
+ doTestHMACDetECDSASample(new SHA1Digest(), privKey, new BigInteger("147D3EB0EDA9F2152DFD014363D6A9CE816D7A1467D326A625FC4AB0C786E1B7" +
+ "4DDF7CD4D0E99541391B266C704BB6B6E8DCCD27B460802E0867143727AA4155" +
+ "55454321EFE5CB6", 16),
+ new BigInteger("17319571CAF533D90D2E78A64060B9C53169AB7FC908947B3EDADC54C79CCF0A" +
+ "7920B4C64A4EAB6282AFE9A459677CDA37FD6DD50BEF18709590FE18B923BDF7" +
+ "4A66B189A850819", 16));
+
+ doTestHMACDetECDSASample(new SHA224Digest(), privKey, new BigInteger("10F4B63E79B2E54E4F4F6A2DBC786D8F4A143ECA7B2AD97810F6472AC6AE2085" +
+ "3222854553BE1D44A7974599DB7061AE8560DF57F2675BE5F9DD94ABAF3D47F1" +
+ "582B318E459748B", 16),
+ new BigInteger("3BBEA07C6B269C2B7FE9AE4DDB118338D0C2F0022920A7F9DCFCB7489594C03B" +
+ "536A9900C4EA6A10410007222D3DAE1A96F291C4C9275D75D98EB290DC0EEF17" +
+ "6037B2C7A7A39A3", 16));
+
+ doTestHMACDetECDSASample(new SHA256Digest(), privKey, new BigInteger("213EF9F3B0CFC4BF996B8AF3A7E1F6CACD2B87C8C63820000800AC787F17EC99" +
+ "C04BCEDF29A8413CFF83142BB88A50EF8D9A086AF4EB03E97C567500C21D8657" +
+ "14D832E03C6D054", 16),
+ new BigInteger("3D32322559B094E20D8935E250B6EC139AC4AAB77920812C119AF419FB62B332" +
+ "C8D226C6C9362AE3C1E4AABE19359B8428EA74EC8FBE83C8618C2BCCB6B43FBA" +
+ "A0F2CCB7D303945", 16));
+
+ doTestHMACDetECDSASample(new SHA384Digest(), privKey, new BigInteger("375D8F49C656A0BBD21D3F54CDA287D853C4BB1849983CD891EF6CD6BB56A62B" +
+ "687807C16685C2C9BCA2663C33696ACCE344C45F3910B1DF806204FF731ECB28" +
+ "9C100EF4D1805EC", 16),
+ new BigInteger("1CDEC6F46DFEEE44BCE71D41C60550DC67CF98D6C91363625AC2553E4368D2DF" +
+ "B734A8E8C72E118A76ACDB0E58697940A0F3DF49E72894BD799450FC9E550CC0" +
+ "4B9FF9B0380021C", 16));
+ doTestHMACDetECDSASample(new SHA512Digest(), privKey, new BigInteger("1C26F40D940A7EAA0EB1E62991028057D91FEDA0366B606F6C434C361F04E545" +
+ "A6A51A435E26416F6838FFA260C617E798E946B57215284182BE55F29A355E60" +
+ "24FE32A47289CF0", 16),
+ new BigInteger("3691DE4369D921FE94EDDA67CB71FBBEC9A436787478063EB1CC778B3DCDC1C4" +
+ "162662752D28DEEDF6F32A269C82D1DB80C87CE4D3B662E03AC347806E3F19D1" +
+ "8D6D4DE7358DF7E", 16));
+
+ doTestHMACDetECDSATest(new SHA1Digest(), privKey, new BigInteger("133F5414F2A9BC41466D339B79376038A64D045E5B0F792A98E5A7AA87E0AD01" +
+ "6419E5F8D176007D5C9C10B5FD9E2E0AB8331B195797C0358BA05ECBF24ACE59" +
+ "C5F368A6C0997CC", 16),
+ new BigInteger("3D16743AE9F00F0B1A500F738719C5582550FEB64689DA241665C4CE4F328BA0" +
+ "E34A7EF527ED13BFA5889FD2D1D214C11EB17D6BC338E05A56F41CAFF1AF7B8D" +
+ "574DB62EF0D0F21", 16));
+
+ doTestHMACDetECDSATest(new SHA224Digest(), privKey, new BigInteger("3048E76506C5C43D92B2E33F62B33E3111CEEB87F6C7DF7C7C01E3CDA28FA5E8" +
+ "BE04B5B23AA03C0C70FEF8F723CBCEBFF0B7A52A3F5C8B84B741B4F6157E69A5" +
+ "FB0524B48F31828", 16),
+ new BigInteger("2C99078CCFE5C82102B8D006E3703E020C46C87C75163A2CD839C885550BA5CB" +
+ "501AC282D29A1C26D26773B60FBE05AAB62BFA0BA32127563D42F7669C97784C" +
+ "8897C22CFB4B8FA", 16));
+
+ doTestHMACDetECDSATest(new SHA256Digest(), privKey, new BigInteger("184BC808506E11A65D628B457FDA60952803C604CC7181B59BD25AEE1411A66D" +
+ "12A777F3A0DC99E1190C58D0037807A95E5080FA1B2E5CCAA37B50D401CFFC34" +
+ "17C005AEE963469", 16),
+ new BigInteger("27280D45F81B19334DBDB07B7E63FE8F39AC7E9AE14DE1D2A6884D2101850289" +
+ "D70EE400F26ACA5E7D73F534A14568478E59D00594981ABE6A1BA18554C13EB5" +
+ "E03921E4DC98333", 16));
+
+ doTestHMACDetECDSATest(new SHA384Digest(), privKey, new BigInteger("319EE57912E7B0FAA1FBB145B0505849A89C6DB1EC06EA20A6A7EDE072A6268A" +
+ "F6FD9C809C7E422A5F33C6C3326EAD7402467DF3272A1B2726C1C20975950F0F" +
+ "50D8324578F13EC", 16),
+ new BigInteger("2CF3EA27EADD0612DD2F96F46E89AB894B01A10DF985C5FC099CFFE0EA083EB4" +
+ "4BE682B08BFE405DAD5F37D0A2C59015BA41027E24B99F8F75A70B6B7385BF39" +
+ "BBEA02513EB880C", 16));
+ doTestHMACDetECDSATest(new SHA512Digest(), privKey, new BigInteger("2AA1888EAB05F7B00B6A784C4F7081D2C833D50794D9FEAF6E22B8BE728A2A90" +
+ "BFCABDC803162020AA629718295A1489EE7ED0ECB8AAA197B9BDFC49D18DDD78" +
+ "FC85A48F9715544", 16),
+ new BigInteger("0AA5371FE5CA671D6ED9665849C37F394FED85D51FEF72DA2B5F28EDFB2C6479" +
+ "CA63320C19596F5E1101988E2C619E302DD05112F47E8823040CE540CD3E90DC" +
+ "F41DBC461744EE9", 16));
+
+ }
+
+ private void doTestHMACDetECDSASample(Digest digest, ECPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+ {
+ doTestHMACDetECDSA(new ECDSASigner(new HMacDSAKCalculator(digest)), digest, SAMPLE, privKey, r, s);
+ }
+
+ private void doTestHMACDetECDSATest(Digest digest, ECPrivateKeyParameters privKey, BigInteger r, BigInteger s)
+ {
+ doTestHMACDetECDSA(new ECDSASigner(new HMacDSAKCalculator(digest)), digest, TEST, privKey, r, s);
+ }
+
+ private void doTestHMACDetECDSA(DSA detSigner, Digest digest, byte[] data, CipherParameters privKey, BigInteger r, BigInteger s)
+ {
+ byte[] m = new byte[digest.getDigestSize()];
+
+ digest.update(data, 0, data.length);
+
+ digest.doFinal(m, 0);
+
+ detSigner.init(true, privKey);
+
+ BigInteger[] rs = detSigner.generateSignature(m);
+
+ if (!r.equals(rs[0]))
+ {
+ fail("r value wrong");
+ }
+ if (!s.equals(rs[1]))
+ {
+ fail("s value wrong");
+ }
+ }
+
+ public String getName()
+ {
+ return "DeterministicDSA";
+ }
+
+ public void performTest()
+ {
+ testHMacDeterministic();
+ testECHMacDeterministic();
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DeterministicDSATest());
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DigestRandomNumberTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DigestRandomNumberTest.java
new file mode 100644
index 00000000..7bcaea8a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DigestRandomNumberTest.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.crypto.prng.DigestRandomGenerator;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.Digest;
+
+public class DigestRandomNumberTest
+ extends SimpleTest
+{
+ private static final byte[] ZERO_SEED = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ private static final byte[] TEST_SEED = Hex.decode("81dcfafc885914057876");
+
+ private static final byte[] expected0SHA1 = Hex.decode("95bca677b3d4ff793213c00892d2356ec729ee02");
+ private static final byte[] noCycle0SHA1 = Hex.decode("d57ccd0eb12c3938d59226412bc1268037b6b846");
+ private static final byte[] expected0SHA256 = Hex.decode("587e2dfd597d086e47ddcd343eac983a5c913bef8c6a1a560a5c1bc3a74b0991");
+ private static final byte[] noCycle0SHA256 = Hex.decode("e5776c4483486ba7be081f4e1b9dafbab25c8fae290fd5474c1ceda2c16f9509");
+ private static final byte[] expected100SHA1 = Hex.decode("b9d924092546e0876cafd4937d7364ebf9efa4be");
+ private static final byte[] expected100SHA256 = Hex.decode("fbc4aa54b948b99de104c44563a552899d718bb75d1941cc62a2444b0506abaf");
+ private static final byte[] expectedTestSHA1 = Hex.decode("e9ecef9f5306daf1ac51a89a211a64cb24415649");
+ private static final byte[] expectedTestSHA256 = Hex.decode("bdab3ca831b472a2fa09bd1bade541ef16c96640a91fcec553679a136061de98");
+
+ private static final byte[] sha1Xors = Hex.decode("7edcc1216934f3891b03ffa65821611a3e2b1f79");
+ private static final byte[] sha256Xors = Hex.decode("5ec48189cc0aa71e79c707bc3c33ffd47bbba368a83d6cfebf3cd3969d7f3eed");
+
+ public String getName()
+ {
+ return "DigestRandomNumber";
+ }
+
+ private void doExpectedTest(Digest digest, int seed, byte[] expected)
+ {
+ doExpectedTest(digest, seed, expected, null);
+ }
+
+ private void doExpectedTest(Digest digest, int seed, byte[] expected, byte[] noCycle)
+ {
+ DigestRandomGenerator rGen = new DigestRandomGenerator(digest);
+ byte[] output = new byte[digest.getDigestSize()];
+
+ rGen.addSeedMaterial(seed);
+
+ for (int i = 0; i != 1024; i++)
+ {
+ rGen.nextBytes(output);
+ }
+
+ if (noCycle != null)
+ {
+ if (Arrays.areEqual(noCycle, output))
+ {
+ fail("seed not being cycled!");
+ }
+ }
+
+ if (!Arrays.areEqual(expected, output))
+ {
+ fail("expected output doesn't match");
+ }
+ }
+
+ private void doExpectedTest(Digest digest, byte[] seed, byte[] expected)
+ {
+ DigestRandomGenerator rGen = new DigestRandomGenerator(digest);
+ byte[] output = new byte[digest.getDigestSize()];
+
+ rGen.addSeedMaterial(seed);
+
+ for (int i = 0; i != 1024; i++)
+ {
+ rGen.nextBytes(output);
+ }
+
+ if (!Arrays.areEqual(expected, output))
+ {
+ fail("expected output doesn't match");
+ }
+ }
+
+ private void doCountTest(Digest digest, byte[] seed, byte[] expectedXors)
+ {
+ DigestRandomGenerator rGen = new DigestRandomGenerator(digest);
+ byte[] output = new byte[digest.getDigestSize()];
+ int[] averages = new int[digest.getDigestSize()];
+ byte[] ands = new byte[digest.getDigestSize()];
+ byte[] xors = new byte[digest.getDigestSize()];
+ byte[] ors = new byte[digest.getDigestSize()];
+
+ rGen.addSeedMaterial(seed);
+
+ for (int i = 0; i != 1000000; i++)
+ {
+ rGen.nextBytes(output);
+ for (int j = 0; j != output.length; j++)
+ {
+ averages[j] += output[j] & 0xff;
+ ands[j] &= output[j];
+ xors[j] ^= output[j];
+ ors[j] |= output[j];
+ }
+ }
+
+ for (int i = 0; i != output.length; i++)
+ {
+ if ((averages[i] / 1000000) != 127)
+ {
+ fail("average test failed for " + digest.getAlgorithmName());
+ }
+ if (ands[i] != 0)
+ {
+ fail("and test failed for " + digest.getAlgorithmName());
+ }
+ if ((ors[i] & 0xff) != 0xff)
+ {
+ fail("or test failed for " + digest.getAlgorithmName());
+ }
+ if (xors[i] != expectedXors[i])
+ {
+ fail("xor test failed for " + digest.getAlgorithmName());
+ }
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ doExpectedTest(new SHA1Digest(), 0, expected0SHA1, noCycle0SHA1);
+ doExpectedTest(new SHA256Digest(), 0, expected0SHA256, noCycle0SHA256);
+
+ doExpectedTest(new SHA1Digest(), 100, expected100SHA1);
+ doExpectedTest(new SHA256Digest(), 100, expected100SHA256);
+
+ doExpectedTest(new SHA1Digest(), ZERO_SEED, expected0SHA1);
+ doExpectedTest(new SHA256Digest(), ZERO_SEED, expected0SHA256);
+
+ doExpectedTest(new SHA1Digest(), TEST_SEED, expectedTestSHA1);
+ doExpectedTest(new SHA256Digest(), TEST_SEED, expectedTestSHA256);
+
+ doCountTest(new SHA1Digest(), TEST_SEED, sha1Xors);
+ doCountTest(new SHA256Digest(), TEST_SEED, sha256Xors);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new DigestRandomNumberTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/DigestTest.java
new file mode 100644
index 00000000..db9b490e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/DigestTest.java
@@ -0,0 +1,226 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.EncodableDigest;
+import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public abstract class DigestTest
+ extends SimpleTest
+{
+ private Digest digest;
+ private String[] input;
+ private String[] results;
+
+ DigestTest(
+ Digest digest,
+ String[] input,
+ String[] results)
+ {
+ this.digest = digest;
+ this.input = input;
+ this.results = results;
+ }
+
+ public String getName()
+ {
+ return digest.getAlgorithmName();
+ }
+
+ public void performTest()
+ {
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i < input.length - 1; i++)
+ {
+ byte[] m = toByteArray(input[i]);
+
+ vectorTest(digest, i, resBuf, m, Hex.decode(results[i]));
+ }
+
+ byte[] lastV = toByteArray(input[input.length - 1]);
+ byte[] lastDigest = Hex.decode(results[input.length - 1]);
+
+ vectorTest(digest, input.length - 1, resBuf, lastV, Hex.decode(results[input.length - 1]));
+
+ testClone(resBuf, lastV, lastDigest);
+ testMemo(resBuf, lastV, lastDigest);
+ if (digest instanceof EncodableDigest)
+ {
+ testEncodedState(resBuf, lastV, lastDigest);
+ }
+ }
+
+ private void testEncodedState(byte[] resBuf, byte[] input, byte[] expected)
+ {
+ // test state encoding;
+ digest.update(input, 0, input.length / 2);
+
+ // copy the Digest
+ Digest copy1 = cloneDigest(((EncodableDigest)digest).getEncodedState());
+ Digest copy2 = cloneDigest(((EncodableDigest)copy1).getEncodedState());
+
+ digest.update(input, input.length / 2, input.length - input.length / 2);
+
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(expected, resBuf))
+ {
+ fail("failing state vector test", expected, new String(Hex.encode(resBuf)));
+ }
+
+ copy1.update(input, input.length / 2, input.length - input.length / 2);
+ copy1.doFinal(resBuf, 0);
+
+ if (!areEqual(expected, resBuf))
+ {
+ fail("failing state copy1 vector test", expected, new String(Hex.encode(resBuf)));
+ }
+
+ copy2.update(input, input.length / 2, input.length - input.length / 2);
+ copy2.doFinal(resBuf, 0);
+
+ if (!areEqual(expected, resBuf))
+ {
+ fail("failing state copy2 vector test", expected, new String(Hex.encode(resBuf)));
+ }
+ }
+
+ private void testMemo(byte[] resBuf, byte[] input, byte[] expected)
+ {
+ Memoable m = (Memoable)digest;
+
+ digest.update(input, 0, input.length/2);
+
+ // copy the Digest
+ Memoable copy1 = m.copy();
+ Memoable copy2 = copy1.copy();
+
+ digest.update(input, input.length/2, input.length - input.length/2);
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(expected, resBuf))
+ {
+ fail("failing memo vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+
+ m.reset(copy1);
+
+ digest.update(input, input.length/2, input.length - input.length/2);
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(expected, resBuf))
+ {
+ fail("failing memo reset vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+
+ Digest md = (Digest)copy2;
+
+ md.update(input, input.length/2, input.length - input.length/2);
+ md.doFinal(resBuf, 0);
+
+ if (!areEqual(expected, resBuf))
+ {
+ fail("failing memo copy vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+ }
+
+ private void testClone(byte[] resBuf, byte[] input, byte[] expected)
+ {
+ digest.update(input, 0, input.length/2);
+
+ // clone the Digest
+ Digest d = cloneDigest(digest);
+
+ digest.update(input, input.length/2, input.length - input.length/2);
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(expected, resBuf))
+ {
+ fail("failing clone vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+
+ d.update(input, input.length/2, input.length - input.length/2);
+ d.doFinal(resBuf, 0);
+
+ if (!areEqual(expected, resBuf))
+ {
+ fail("failing second clone vector test", results[results.length - 1], new String(Hex.encode(resBuf)));
+ }
+ }
+
+ protected byte[] toByteArray(String input)
+ {
+ byte[] bytes = new byte[input.length()];
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ bytes[i] = (byte)input.charAt(i);
+ }
+
+ return bytes;
+ }
+
+ private void vectorTest(
+ Digest digest,
+ int count,
+ byte[] resBuf,
+ byte[] input,
+ byte[] expected)
+ {
+ digest.update(input, 0, input.length);
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, expected))
+ {
+ fail("Vector " + count + " failed got " + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ protected abstract Digest cloneDigest(Digest digest);
+
+ protected Digest cloneDigest(byte[] encodedState)
+ {
+ throw new IllegalStateException("Unsupported");
+ }
+
+ //
+ // optional tests
+ //
+ protected void millionATest(
+ String expected)
+ {
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i < 1000000; i++)
+ {
+ digest.update((byte)'a');
+ }
+
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, Hex.decode(expected)))
+ {
+ fail("Million a's failed", expected, new String(Hex.encode(resBuf)));
+ }
+ }
+
+ protected void sixtyFourKTest(
+ String expected)
+ {
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i < 65536; i++)
+ {
+ digest.update((byte)(i & 0xff));
+ }
+
+ digest.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, Hex.decode(expected)))
+ {
+ fail("64k test failed", expected, new String(Hex.encode(resBuf)));
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/EAXTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/EAXTest.java
new file mode 100644
index 00000000..401d9fa3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/EAXTest.java
@@ -0,0 +1,355 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.EAXBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class EAXTest
+ extends SimpleTest
+{
+ private byte[] K1 = Hex.decode("233952DEE4D5ED5F9B9C6D6FF80FF478");
+ private byte[] N1 = Hex.decode("62EC67F9C3A4A407FCB2A8C49031A8B3");
+ private byte[] A1 = Hex.decode("6BFB914FD07EAE6B");
+ private byte[] P1 = Hex.decode("");
+ private byte[] C1 = Hex.decode("E037830E8389F27B025A2D6527E79D01");
+ private byte[] T1 = Hex.decode("E037830E8389F27B025A2D6527E79D01");
+
+ private byte[] K2 = Hex.decode("91945D3F4DCBEE0BF45EF52255F095A4");
+ private byte[] N2 = Hex.decode("BECAF043B0A23D843194BA972C66DEBD");
+ private byte[] A2 = Hex.decode("FA3BFD4806EB53FA");
+ private byte[] P2 = Hex.decode("F7FB");
+ private byte[] C2 = Hex.decode("19DD5C4C9331049D0BDAB0277408F67967E5");
+ private byte[] T2 = Hex.decode("5C4C9331049D0BDAB0277408F67967E5");
+
+ private byte[] K3 = Hex.decode("01F74AD64077F2E704C0F60ADA3DD523");
+ private byte[] N3 = Hex.decode("70C3DB4F0D26368400A10ED05D2BFF5E");
+ private byte[] A3 = Hex.decode("234A3463C1264AC6");
+ private byte[] P3 = Hex.decode("1A47CB4933");
+ private byte[] C3 = Hex.decode("D851D5BAE03A59F238A23E39199DC9266626C40F80");
+ private byte[] T3 = Hex.decode("3A59F238A23E39199DC9266626C40F80");
+
+ private byte[] K4 = Hex.decode("D07CF6CBB7F313BDDE66B727AFD3C5E8");
+ private byte[] N4 = Hex.decode("8408DFFF3C1A2B1292DC199E46B7D617");
+ private byte[] A4 = Hex.decode("33CCE2EABFF5A79D");
+ private byte[] P4 = Hex.decode("481C9E39B1");
+ private byte[] C4 = Hex.decode("632A9D131AD4C168A4225D8E1FF755939974A7BEDE");
+ private byte[] T4 = Hex.decode("D4C168A4225D8E1FF755939974A7BEDE");
+
+ private byte[] K5 = Hex.decode("35B6D0580005BBC12B0587124557D2C2");
+ private byte[] N5 = Hex.decode("FDB6B06676EEDC5C61D74276E1F8E816");
+ private byte[] A5 = Hex.decode("AEB96EAEBE2970E9");
+ private byte[] P5 = Hex.decode("40D0C07DA5E4");
+ private byte[] C5 = Hex.decode("071DFE16C675CB0677E536F73AFE6A14B74EE49844DD");
+ private byte[] T5 = Hex.decode("CB0677E536F73AFE6A14B74EE49844DD");
+
+ private byte[] K6 = Hex.decode("BD8E6E11475E60B268784C38C62FEB22");
+ private byte[] N6 = Hex.decode("6EAC5C93072D8E8513F750935E46DA1B");
+ private byte[] A6 = Hex.decode("D4482D1CA78DCE0F");
+ private byte[] P6 = Hex.decode("4DE3B35C3FC039245BD1FB7D");
+ private byte[] C6 = Hex.decode("835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F");
+ private byte[] T6 = Hex.decode("ABB8644FD6CCB86947C5E10590210A4F");
+
+ private byte[] K7 = Hex.decode("7C77D6E813BED5AC98BAA417477A2E7D");
+ private byte[] N7 = Hex.decode("1A8C98DCD73D38393B2BF1569DEEFC19");
+ private byte[] A7 = Hex.decode("65D2017990D62528");
+ private byte[] P7 = Hex.decode("8B0A79306C9CE7ED99DAE4F87F8DD61636");
+ private byte[] C7 = Hex.decode("02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2");
+ private byte[] T7 = Hex.decode("137327D10649B0AA6E1C181DB617D7F2");
+
+ private byte[] K8 = Hex.decode("5FFF20CAFAB119CA2FC73549E20F5B0D");
+ private byte[] N8 = Hex.decode("DDE59B97D722156D4D9AFF2BC7559826");
+ private byte[] A8 = Hex.decode("54B9F04E6A09189A");
+ private byte[] P8 = Hex.decode("1BDA122BCE8A8DBAF1877D962B8592DD2D56");
+ private byte[] C8 = Hex.decode("2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A");
+ private byte[] T8 = Hex.decode("3B60450599BD02C96382902AEF7F832A");
+
+ private byte[] K9 = Hex.decode("A4A4782BCFFD3EC5E7EF6D8C34A56123");
+ private byte[] N9 = Hex.decode("B781FCF2F75FA5A8DE97A9CA48E522EC");
+ private byte[] A9 = Hex.decode("899A175897561D7E");
+ private byte[] P9 = Hex.decode("6CF36720872B8513F6EAB1A8A44438D5EF11");
+ private byte[] C9 = Hex.decode("0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700");
+ private byte[] T9 = Hex.decode("E7F6D2231618102FDB7FE55FF1991700");
+
+ private byte[] K10 = Hex.decode("8395FCF1E95BEBD697BD010BC766AAC3");
+ private byte[] N10 = Hex.decode("22E7ADD93CFC6393C57EC0B3C17D6B44");
+ private byte[] A10 = Hex.decode("126735FCC320D25A");
+ private byte[] P10 = Hex.decode("CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7");
+ private byte[] C10 = Hex.decode("CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E");
+ private byte[] T10 = Hex.decode("CFC46AFC253B4652B1AF3795B124AB6E");
+
+ private byte[] K11 = Hex.decode("8395FCF1E95BEBD697BD010BC766AAC3");
+ private byte[] N11 = Hex.decode("22E7ADD93CFC6393C57EC0B3C17D6B44");
+ private byte[] A11 = Hex.decode("126735FCC320D25A");
+ private byte[] P11 = Hex.decode("CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7");
+ private byte[] C11 = Hex.decode("CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC");
+ private byte[] T11 = Hex.decode("CFC46AFC");
+
+ private static final int NONCE_LEN = 8;
+ private static final int MAC_LEN = 8;
+ private static final int AUTHEN_LEN = 20;
+
+ public String getName()
+ {
+ return "EAX";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ checkVectors(1, K1, 128, N1, A1, P1, T1, C1);
+ checkVectors(2, K2, 128, N2, A2, P2, T2, C2);
+ checkVectors(3, K3, 128, N3, A3, P3, T3, C3);
+ checkVectors(4, K4, 128, N4, A4, P4, T4, C4);
+ checkVectors(5, K5, 128, N5, A5, P5, T5, C5);
+ checkVectors(6, K6, 128, N6, A6, P6, T6, C6);
+ checkVectors(7, K7, 128, N7, A7, P7, T7, C7);
+ checkVectors(8, K8, 128, N8, A8, P8, T8, C8);
+ checkVectors(9, K9, 128, N9, A9, P9, T9, C9);
+ checkVectors(10, K10, 128, N10, A10, P10, T10, C10);
+ checkVectors(11, K11, 32, N11, A11, P11, T11, C11);
+
+ EAXBlockCipher eax = new EAXBlockCipher(new AESFastEngine());
+ ivParamTest(1, eax, K1, N1);
+
+ //
+ // exception tests
+ //
+
+ try
+ {
+ eax.init(false, new AEADParameters(new KeyParameter(K1), 32, N2, A2));
+
+ byte[] enc = new byte[C2.length];
+ int len = eax.processBytes(C2, 0, C2.length, enc, 0);
+
+ len += eax.doFinal(enc, len);
+
+ fail("invalid cipher text not picked up");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ eax.init(false, new KeyParameter(K1));
+
+ fail("illegal argument not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ randomTests();
+ AEADTestUtil.testReset(this, new EAXBlockCipher(new AESEngine()), new EAXBlockCipher(new AESEngine()), new AEADParameters(new KeyParameter(K1), 32, N2));
+ AEADTestUtil.testTampering(this, eax, new AEADParameters(new KeyParameter(K1), 32, N2));
+ AEADTestUtil.testOutputSizes(this, new EAXBlockCipher(new AESEngine()), new AEADParameters(
+ new KeyParameter(K1), 32, N2));
+ AEADTestUtil.testBufferSizeChecks(this, new EAXBlockCipher(new AESEngine()), new AEADParameters(
+ new KeyParameter(K1), 32, N2));
+ }
+
+ private void checkVectors(
+ int count,
+ byte[] k,
+ int macSize,
+ byte[] n,
+ byte[] a,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ byte[] fa = new byte[a.length / 2];
+ byte[] la = new byte[a.length - (a.length / 2)];
+ System.arraycopy(a, 0, fa, 0, fa.length);
+ System.arraycopy(a, fa.length, la, 0, la.length);
+
+ checkVectors(count, "all initial associated data", k, macSize, n, a, null, p, t, c);
+ checkVectors(count, "subsequent associated data", k, macSize, n, null, a, p, t, c);
+ checkVectors(count, "split associated data", k, macSize, n, fa, la, p, t, c);
+ }
+
+ private void checkVectors(
+ int count,
+ String additionalDataType,
+ byte[] k,
+ int macSize,
+ byte[] n,
+ byte[] a,
+ byte[] sa,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ EAXBlockCipher encEax = new EAXBlockCipher(new AESFastEngine());
+ EAXBlockCipher decEax = new EAXBlockCipher(new AESFastEngine());
+
+ AEADParameters parameters = new AEADParameters(new KeyParameter(k), macSize, n, a);
+ encEax.init(true, parameters);
+ decEax.init(false, parameters);
+
+ runCheckVectors(count, encEax, decEax, additionalDataType, sa, p, t, c);
+ runCheckVectors(count, encEax, decEax, additionalDataType, sa, p, t, c);
+
+ // key reuse test
+ parameters = new AEADParameters(null, macSize, n, a);
+ encEax.init(true, parameters);
+ decEax.init(false, parameters);
+
+ runCheckVectors(count, encEax, decEax, additionalDataType, sa, p, t, c);
+ runCheckVectors(count, encEax, decEax, additionalDataType, sa, p, t, c);
+ }
+
+ private void runCheckVectors(
+ int count,
+ EAXBlockCipher encEax,
+ EAXBlockCipher decEax,
+ String additionalDataType,
+ byte[] sa,
+ byte[] p,
+ byte[] t,
+ byte[] c)
+ throws InvalidCipherTextException
+ {
+ byte[] enc = new byte[c.length];
+
+ if (sa != null)
+ {
+ encEax.processAADBytes(sa, 0, sa.length);
+ }
+
+ int len = encEax.processBytes(p, 0, p.length, enc, 0);
+
+ len += encEax.doFinal(enc, len);
+
+ if (!areEqual(c, enc))
+ {
+ fail("encrypted stream fails to match in test " + count + " with " + additionalDataType);
+ }
+
+ byte[] tmp = new byte[enc.length];
+
+ if (sa != null)
+ {
+ decEax.processAADBytes(sa, 0, sa.length);
+ }
+
+ len = decEax.processBytes(enc, 0, enc.length, tmp, 0);
+
+ len += decEax.doFinal(tmp, len);
+
+ byte[] dec = new byte[len];
+
+ System.arraycopy(tmp, 0, dec, 0, len);
+
+ if (!areEqual(p, dec))
+ {
+ fail("decrypted stream fails to match in test " + count + " with " + additionalDataType);
+ }
+
+ if (!areEqual(t, decEax.getMac()))
+ {
+ fail("MAC fails to match in test " + count + " with " + additionalDataType);
+ }
+ }
+
+ private void ivParamTest(
+ int count,
+ AEADBlockCipher eax,
+ byte[] k,
+ byte[] n)
+ throws InvalidCipherTextException
+ {
+ byte[] p = Strings.toByteArray("hello world!!");
+
+ eax.init(true, new ParametersWithIV(new KeyParameter(k), n));
+
+ byte[] enc = new byte[p.length + 8];
+
+ int len = eax.processBytes(p, 0, p.length, enc, 0);
+
+ len += eax.doFinal(enc, len);
+
+ eax.init(false, new ParametersWithIV(new KeyParameter(k), n));
+
+ byte[] tmp = new byte[enc.length];
+
+ len = eax.processBytes(enc, 0, enc.length, tmp, 0);
+
+ len += eax.doFinal(tmp, len);
+
+ byte[] dec = new byte[len];
+
+ System.arraycopy(tmp, 0, dec, 0, len);
+
+ if (!areEqual(p, dec))
+ {
+ fail("decrypted stream fails to match in test " + count);
+ }
+ }
+
+ private void randomTests()
+ throws InvalidCipherTextException
+ {
+ SecureRandom srng = new SecureRandom();
+ for (int i = 0; i < 10; ++i)
+ {
+ randomTest(srng);
+ }
+ }
+
+ private void randomTest(
+ SecureRandom srng)
+ throws InvalidCipherTextException
+ {
+ int DAT_LEN = srng.nextInt() >>> 22; // Note: JDK1.0 compatibility
+ byte[] nonce = new byte[NONCE_LEN];
+ byte[] authen = new byte[AUTHEN_LEN];
+ byte[] datIn = new byte[DAT_LEN];
+ byte[] key = new byte[16];
+ srng.nextBytes(nonce);
+ srng.nextBytes(authen);
+ srng.nextBytes(datIn);
+ srng.nextBytes(key);
+
+ AESFastEngine engine = new AESFastEngine();
+ KeyParameter sessKey = new KeyParameter(key);
+ EAXBlockCipher eaxCipher = new EAXBlockCipher(engine);
+
+ AEADParameters params = new AEADParameters(sessKey, MAC_LEN * 8, nonce, authen);
+ eaxCipher.init(true, params);
+
+ byte[] intrDat = new byte[eaxCipher.getOutputSize(datIn.length)];
+ int outOff = eaxCipher.processBytes(datIn, 0, DAT_LEN, intrDat, 0);
+ outOff += eaxCipher.doFinal(intrDat, outOff);
+
+ eaxCipher.init(false, params);
+ byte[] datOut = new byte[eaxCipher.getOutputSize(outOff)];
+ int resultLen = eaxCipher.processBytes(intrDat, 0, outOff, datOut, 0);
+ eaxCipher.doFinal(datOut, resultLen);
+
+ if (!areEqual(datIn, datOut))
+ {
+ fail("EAX roundtrip failed to match");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new EAXTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECDHKEKGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECDHKEKGeneratorTest.java
new file mode 100644
index 00000000..d75b1396
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECDHKEKGeneratorTest.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.agreement.kdf.DHKDFParameters;
+import org.bouncycastle.crypto.agreement.kdf.ECDHKEKGenerator;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * ECDHKEK Generator tests.
+ */
+public class ECDHKEKGeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("db4a8daba1f98791d54e940175dd1a5f3a0826a1066aa9b668d4dc1e1e0790158dcad1533c03b44214d1b61fefa8b579");
+ private ASN1ObjectIdentifier alg1 = NISTObjectIdentifiers.id_aes256_wrap;
+ private byte[] result1 = Hex.decode("8ecc6d85caf25eaba823a7d620d4ab0d33e4c645f2");
+
+ private byte[] seed2 = Hex.decode("75d7487b5d3d2bfb3c69ce0365fe64e3bfab5d0d63731628a9f47eb8fddfa28c65decaf228a0b38f0c51c6a3356d7c56");
+ private ASN1ObjectIdentifier alg2 = NISTObjectIdentifiers.id_aes128_wrap;
+ private byte[] result2 = Hex.decode("042be1faca3a4a8fc859241bfb87ba35");
+
+ private byte[] seed3 = Hex.decode("fdeb6d809f997e8ac174d638734dc36d37aaf7e876e39967cd82b1cada3de772449788461ee7f856bad9305627f8e48b");
+ private ASN1ObjectIdentifier alg3 = PKCSObjectIdentifiers.id_alg_CMS3DESwrap;
+ private byte[] result3 = Hex.decode("bcd701fc92109b1b9d6f3b6497ad5ca9627fa8a597010305");
+
+ public ECDHKEKGeneratorTest()
+ {
+ }
+
+ public void performTest()
+ {
+ checkMask(1, new ECDHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg1, 256, seed1), result1);
+ checkMask(2, new ECDHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg2, 128, seed2), result2);
+ checkMask(3, new ECDHKEKGenerator(new SHA1Digest()), new DHKDFParameters(alg3, 192, seed3), result3);
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ DerivationParameters params,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(params);
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("ECDHKEKGenerator failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "ECDHKEKGenerator";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECDHKEKGeneratorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECGOST3410Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECGOST3410Test.java
new file mode 100644
index 00000000..175835cd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECGOST3410Test.java
@@ -0,0 +1,327 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.digests.GOST3411Digest;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.ECGOST3410Signer;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.FixedSecureRandom;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * ECGOST3410 tests are taken from GOST R 34.10-2001.
+ */
+public class ECGOST3410Test
+ extends SimpleTest
+ {
+ byte[] hashmessage = Hex.decode("3042453136414534424341374533364339313734453431443642453241453435");
+
+ /**
+ * ECGOST3410 over the field Fp<br>
+ */
+ BigInteger r = new BigInteger("29700980915817952874371204983938256990422752107994319651632687982059210933395");
+ BigInteger s = new BigInteger("574973400270084654178925310019147038455227042649098563933718999175515839552");
+
+ byte[] kData = new BigInteger("53854137677348463731403841147996619241504003434302020712960838528893196233395").toByteArray();
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ private void ecGOST3410_TEST()
+ {
+ BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564821041"); //p
+ BigInteger mod_q = new BigInteger("57896044618658097711785492504343953927082934583725450622380973592137631069619");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("7"), // a
+ new BigInteger("43308876546767276905765904595650931995942111794451039583252968842033849580414"), // b
+ mod_q, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("2"), // x
+ new BigInteger("4018974056539037503335449422937059775635739389905545080690979365213431566280")), // y
+ mod_q);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("55441196065363246126355624130324183196576709222340016572108097750006097525544"), // d
+ params);
+
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ byte[] mVal = new BigInteger("20798893674476452017134061561508270130637142515379653289952617252661468872421").toByteArray();
+ byte[] message = new byte[mVal.length];
+
+ for (int i = 0; i != mVal.length; i++)
+ {
+ message[i] = mVal[mVal.length - 1 - i];
+ }
+
+ BigInteger[] sig = ecgost3410.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong.", r, sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong.", s, sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.createPoint(
+ new BigInteger("57520216126176808443631405023338071176630104906313632182896741342206604859403"), // x
+ new BigInteger("17614944419213781543809391949654080031942662045363639260709847859438286763994")), // y
+ params);
+
+ ecgost3410.init(false, pubKey);
+ if (!ecgost3410.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("verification fails");
+ }
+ }
+
+ /**
+ * Test Sign & Verify with test parameters
+ * see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
+ * gostR3410-2001-TestParamSet P.46
+ */
+ private void ecGOST3410_TestParam()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564821041"); //p
+ BigInteger mod_q = new BigInteger("57896044618658097711785492504343953927082934583725450622380973592137631069619");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("7"), // a
+ new BigInteger("43308876546767276905765904595650931995942111794451039583252968842033849580414"), // b
+ mod_q, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("2"), // x
+ new BigInteger("4018974056539037503335449422937059775635739389905545080690979365213431566280")), // y
+ mod_q);
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ //get hash message using the digest GOST3411.
+ byte[] message = "Message for sign".getBytes();
+ GOST3411Digest gost3411 = new GOST3411Digest();
+ gost3411.update(message, 0, message.length);
+ byte[] hashmessage = new byte[gost3411.getDigestSize()];
+ gost3411.doFinal(hashmessage, 0);
+
+ BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
+
+ ecgost3410.init(false, pair.getPublic());
+
+ if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * Test Sign & Verify with A parameters
+ * see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
+ * gostR3410-2001-CryptoPro-A-ParamSet P.47
+ */
+ public void ecGOST3410_AParam()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); //p
+ BigInteger mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a
+ new BigInteger("166"), // b
+ mod_q, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("1"), // x
+ new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y
+ mod_q);
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
+
+ ecgost3410.init(false, pair.getPublic());
+
+ if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * Test Sign & Verify with B parameters
+ * see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
+ * gostR3410-2001-CryptoPro-B-ParamSet P.47-48
+ */
+ private void ecGOST3410_BParam()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823193"); //p
+ BigInteger mod_q = new BigInteger("57896044618658097711785492504343953927102133160255826820068844496087732066703");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823190"), // a
+ new BigInteger("28091019353058090096996979000309560759124368558014865957655842872397301267595"), // b
+ mod_q, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("1"), // x
+ new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124")), // y
+ mod_q);
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
+
+ ecgost3410.init(false, pair.getPublic());
+
+ if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * Test Sign & Verify with C parameters
+ * see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
+ * gostR3410-2001-CryptoPro-C-ParamSet P.48
+ */
+ private void ecGOST3410_CParam()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619"); //p
+ BigInteger mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ mod_p, // p
+ new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), // a
+ new BigInteger("32858"), // b
+ mod_q, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.createPoint(
+ new BigInteger("0"), // x
+ new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")), // y
+ mod_q);
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();
+
+ ecgost3410.init(true, param);
+
+ BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
+
+ ecgost3410.init(false, pair.getPublic());
+
+ if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ public String getName()
+ {
+ return "ECGOST3410";
+ }
+
+ public void performTest()
+ {
+ ecGOST3410_TEST();
+ ecGOST3410_TestParam();
+ ecGOST3410_AParam();
+ ecGOST3410_BParam();
+ ecGOST3410_CParam();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECGOST3410Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java
new file mode 100644
index 00000000..64d19f50
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
+import org.bouncycastle.crypto.kems.ECIESKeyEncapsulation;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Tests for the ECIES Key Encapsulation Mechanism
+ */
+public class ECIESKeyEncapsulationTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "ECIESKeyEncapsulation";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+
+ // Set EC domain parameters and generate key pair
+ X9ECParameters spec = SECNamedCurves.getByName("secp224r1");
+ ECDomainParameters ecDomain = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN());
+ ECKeyPairGenerator ecGen = new ECKeyPairGenerator();
+
+ ecGen.init(new ECKeyGenerationParameters(ecDomain, new SecureRandom()));
+
+ AsymmetricCipherKeyPair keys = ecGen.generateKeyPair();
+
+ // Set ECIES-KEM parameters
+ ECIESKeyEncapsulation kem;
+ KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA1Digest());
+ SecureRandom rnd = new SecureRandom();
+ byte[] out = new byte[57];
+ KeyParameter key1, key2;
+
+ // Test basic ECIES-KEM
+ kem = new ECIESKeyEncapsulation(kdf, rnd);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed basic test");
+ }
+
+ // Test ECIES-KEM using new cofactor mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, true, false, false);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed cofactor test");
+ }
+
+ // Test ECIES-KEM using old cofactor mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, false, true, false);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed old cofactor test");
+ }
+
+ // Test ECIES-KEM using single hash mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, false, false, true);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed single hash test");
+ }
+
+ // Test ECIES-KEM using new cofactor mode and single hash mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, true, false, true);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed cofactor and single hash test");
+ }
+
+ // Test ECIES-KEM using old cofactor mode and single hash mode
+ kem = new ECIESKeyEncapsulation(kdf, rnd, false, true, true);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed old cofactor and single hash test");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECIESKeyEncapsulationTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESTest.java
new file mode 100644
index 00000000..8cf100b7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECIESTest.java
@@ -0,0 +1,379 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.KeyEncoder;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.engines.IESEngine;
+import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;
+import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.IESParameters;
+import org.bouncycastle.crypto.params.IESWithCipherParameters;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.parsers.ECIESPublicKeyParser;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * test for ECIES - Elliptic Curve Integrated Encryption Scheme
+ */
+public class ECIESTest
+ extends SimpleTest
+{
+ private static byte[] TWOFISH_IV = Hex.decode("000102030405060708090a0b0c0d0e0f");
+
+ ECIESTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "ECIES";
+ }
+
+ private void doStaticTest(byte[] iv)
+ throws Exception
+ {
+ BigInteger n = new BigInteger("6277101735386680763835789423176059013767194773182842284081");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ n);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ AsymmetricCipherKeyPair p1 = new AsymmetricCipherKeyPair(pubKey, priKey);
+ AsymmetricCipherKeyPair p2 = new AsymmetricCipherKeyPair(pubKey, priKey);
+
+ //
+ // stream test
+ //
+ IESEngine i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ IESEngine i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ byte[] d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ byte[] e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ CipherParameters p = new IESParameters(d, e, 64);
+
+ i1.init(true, p1.getPrivate(), p2.getPublic(), p);
+ i2.init(false, p2.getPrivate(), p1.getPublic(), p);
+
+ byte[] message = Hex.decode("1234567890abcdef");
+
+ byte[] out1 = i1.processBlock(message, 0, message.length);
+
+ if (!areEqual(out1, Hex.decode("468d89877e8238802403ec4cb6b329faeccfa6f3a730f2cdb3c0a8e8")))
+ {
+ fail("stream cipher test failed on enc");
+ }
+
+ byte[] out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("stream cipher test failed");
+ }
+
+ //
+ // twofish with CBC
+ //
+ BufferedBlockCipher c1 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ BufferedBlockCipher c2 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c1);
+ i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c2);
+ d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ p = new IESWithCipherParameters(d, e, 64, 128);
+
+ if (iv != null)
+ {
+ p = new ParametersWithIV(p, iv);
+ }
+
+ i1.init(true, p1.getPrivate(), p2.getPublic(), p);
+ i2.init(false, p2.getPrivate(), p1.getPublic(), p);
+
+ message = Hex.decode("1234567890abcdef");
+
+ out1 = i1.processBlock(message, 0, message.length);
+
+ if (!areEqual(out1, (iv == null) ?
+ Hex.decode("b8a06ea5c2b9df28b58a0a90a734cde8c9c02903e5c220021fe4417410d1e53a32a71696")
+ : Hex.decode("f246b0e26a2711992cac9c590d08e45c5e730b7c0f4218bb064e27b7dd7c8a3bd8bf01c3")))
+ {
+ fail("twofish cipher test failed on enc");
+ }
+
+ out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("twofish cipher test failed");
+ }
+ }
+
+ private void doEphemeralTest(byte[] iv, final boolean usePointCompression)
+ throws Exception
+ {
+ BigInteger n = new BigInteger("6277101735386680763835789423176059013767194773182842284081");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ n);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ AsymmetricCipherKeyPair p1 = new AsymmetricCipherKeyPair(pubKey, priKey);
+ AsymmetricCipherKeyPair p2 = new AsymmetricCipherKeyPair(pubKey, priKey);
+
+ // Generate the ephemeral key pair
+ ECKeyPairGenerator gen = new ECKeyPairGenerator();
+ gen.init(new ECKeyGenerationParameters(params, new SecureRandom()));
+
+ EphemeralKeyPairGenerator ephKeyGen = new EphemeralKeyPairGenerator(gen, new KeyEncoder()
+ {
+ public byte[] getEncoded(AsymmetricKeyParameter keyParameter)
+ {
+ return ((ECPublicKeyParameters)keyParameter).getQ().getEncoded(usePointCompression);
+ }
+ });
+
+ //
+ // stream test
+ //
+ IESEngine i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ IESEngine i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+
+ byte[] d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ byte[] e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ CipherParameters p = new IESParameters(d, e, 64);
+
+ i1.init(p2.getPublic(), p, ephKeyGen);
+ i2.init(p2.getPrivate(), p, new ECIESPublicKeyParser(params));
+
+ byte[] message = Hex.decode("1234567890abcdef");
+
+ byte[] out1 = i1.processBlock(message, 0, message.length);
+
+ byte[] out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("stream cipher test failed");
+ }
+
+ //
+ // twofish with CBC
+ //
+ BufferedBlockCipher c1 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ BufferedBlockCipher c2 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c1);
+ i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c2);
+ d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ p = new IESWithCipherParameters(d, e, 64, 128);
+
+ if (iv != null)
+ {
+ p = new ParametersWithIV(p, iv);
+ }
+
+ i1.init(p2.getPublic(), p, ephKeyGen);
+ i2.init(p2.getPrivate(), p, new ECIESPublicKeyParser(params));
+
+ message = Hex.decode("1234567890abcdef");
+
+ out1 = i1.processBlock(message, 0, message.length);
+
+ out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("twofish cipher test failed");
+ }
+ }
+
+ private void doTest(AsymmetricCipherKeyPair p1, AsymmetricCipherKeyPair p2)
+ throws Exception
+ {
+ //
+ // stream test
+ //
+ IESEngine i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ IESEngine i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()));
+ byte[] d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ byte[] e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ IESParameters p = new IESParameters(d, e, 64);
+
+ i1.init(true, p1.getPrivate(), p2.getPublic(), p);
+ i2.init(false, p2.getPrivate(), p1.getPublic(), p);
+
+ byte[] message = Hex.decode("1234567890abcdef");
+
+ byte[] out1 = i1.processBlock(message, 0, message.length);
+
+ byte[] out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("stream cipher test failed");
+ }
+
+ //
+ // twofish with CBC
+ //
+ BufferedBlockCipher c1 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ BufferedBlockCipher c2 = new PaddedBufferedBlockCipher(
+ new CBCBlockCipher(new TwofishEngine()));
+ i1 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c1);
+ i2 = new IESEngine(
+ new ECDHBasicAgreement(),
+ new KDF2BytesGenerator(new SHA1Digest()),
+ new HMac(new SHA1Digest()),
+ c2);
+ d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
+ e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
+ p = new IESWithCipherParameters(d, e, 64, 128);
+
+ i1.init(true, p1.getPrivate(), p2.getPublic(), p);
+ i2.init(false, p2.getPrivate(), p1.getPublic(), p);
+
+ message = Hex.decode("1234567890abcdef");
+
+ out1 = i1.processBlock(message, 0, message.length);
+
+ out2 = i2.processBlock(out1, 0, out1.length);
+
+ if (!areEqual(out2, message))
+ {
+ fail("twofish cipher test failed");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ doStaticTest(null);
+ doStaticTest(TWOFISH_IV);
+
+ BigInteger n = new BigInteger("6277101735386680763835789423176059013767194773182842284081");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ n);
+
+ ECKeyPairGenerator eGen = new ECKeyPairGenerator();
+ KeyGenerationParameters gParam = new ECKeyGenerationParameters(params, new SecureRandom());
+
+ eGen.init(gParam);
+
+ AsymmetricCipherKeyPair p1 = eGen.generateKeyPair();
+ AsymmetricCipherKeyPair p2 = eGen.generateKeyPair();
+
+ doTest(p1, p2);
+
+ doEphemeralTest(null, false);
+ doEphemeralTest(null, true);
+ doEphemeralTest(TWOFISH_IV, false);
+ doEphemeralTest(TWOFISH_IV, true);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECIESTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECNRTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECNRTest.java
new file mode 100644
index 00000000..d4cf7419
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECNRTest.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.ECNRSigner;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.FixedSecureRandom;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * ECNR tests.
+ */
+public class ECNRTest
+ extends SimpleTest
+{
+ /**
+ * a basic regression test with 239 bit prime
+ */
+ BigInteger r = new BigInteger("308636143175167811492623515537541734843573549327605293463169625072911693");
+ BigInteger s = new BigInteger("852401710738814635664888632022555967400445256405412579597015412971797143");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("700000017569056646655505781757157107570501575775705779575555657156756655"));
+
+ SecureRandom k = new FixedSecureRandom(true, kData);
+
+ private void ecNR239bitPrime()
+ {
+ BigInteger n = new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ n);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
+ params);
+
+ ECNRSigner ecnr = new ECNRSigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecnr.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecnr.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong.", r, sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong.", s, sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("025b6dc53bc61a2548ffb0f671472de6c9521a9d2d2534e65abfcbd5fe0c70")), // Q
+ params);
+
+ ecnr.init(false, pubKey);
+ if (!ecnr.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ public String getName()
+ {
+ return "ECNR";
+ }
+
+ public void performTest()
+ {
+ ecNR239bitPrime();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECNRTest());
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ECTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECTest.java
new file mode 100644
index 00000000..fc11276a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ECTest.java
@@ -0,0 +1,926 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.BasicAgreement;
+import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
+import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
+import org.bouncycastle.crypto.agreement.ECMQVBasicAgreement;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.params.MQVPrivateParameters;
+import org.bouncycastle.crypto.params.MQVPublicParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.FixedSecureRandom;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * ECDSA tests are taken from X9.62.
+ */
+public class ECTest
+ extends SimpleTest
+{
+ /**
+ * X9.62 - 1998,<br>
+ * J.3.1, Page 152, ECDSA over the field Fp<br>
+ * an example with 192 bit prime
+ */
+ private void testECDSA192bitPrime()
+ {
+ BigInteger r = new BigInteger("3342403536405981729393488334694600415596881826869351677613");
+ BigInteger s = new BigInteger("5735822328888155254683894997897571951568553642892029982342");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("6140507067065001063065065565667405560006161556565665656654"));
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ BigInteger n = new BigInteger("6277101735386680763835789423176059013767194773182842284081");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), // G
+ n);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("651056770906015076056810763456358567190100156695615665659"), // d
+ params);
+
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("0262b12d60690cdcf330babab6e69763b471f994dd702d16a5")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("verification fails");
+ }
+ }
+
+ private void decodeTest()
+ {
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("6277101735386680763835789423207666416083908700390324961279"), // q
+ new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), // a
+ new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16)); // b
+
+ ECPoint p = curve.decodePoint(Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")).normalize();
+
+ if (!p.getAffineXCoord().toBigInteger().equals(new BigInteger("188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", 16)))
+ {
+ fail("x uncompressed incorrectly");
+ }
+
+ if (!p.getAffineYCoord().toBigInteger().equals(new BigInteger("7192b95ffc8da78631011ed6b24cdd573f977a11e794811", 16)))
+ {
+ fail("y uncompressed incorrectly");
+ }
+
+ byte[] encoding = p.getEncoded();
+
+ if (!areEqual(encoding, Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")))
+ {
+ fail("point compressed incorrectly");
+ }
+ }
+
+ /**
+ * X9.62 - 1998,<br>
+ * J.3.2, Page 155, ECDSA over the field Fp<br>
+ * an example with 239 bit prime
+ */
+ private void testECDSA239bitPrime()
+ {
+ BigInteger r = new BigInteger("308636143175167811492622547300668018854959378758531778147462058306432176");
+ BigInteger s = new BigInteger("323813553209797357708078776831250505931891051755007842781978505179448783");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("700000017569056646655505781757157107570501575775705779575555657156756655"));
+
+ SecureRandom k = new FixedSecureRandom(true, kData);
+
+ BigInteger n = new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ n);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
+ params);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("025b6dc53bc61a2548ffb0f671472de6c9521a9d2d2534e65abfcbd5fe0c70")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+
+ /**
+ * X9.62 - 1998,<br>
+ * J.2.1, Page 100, ECDSA over the field F2m<br>
+ * an example with 191 bit binary field
+ */
+ private void testECDSA191bitBinary()
+ {
+ BigInteger r = new BigInteger("87194383164871543355722284926904419997237591535066528048");
+ BigInteger s = new BigInteger("308992691965804947361541664549085895292153777025772063598");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("1542725565216523985789236956265265265235675811949404040041"));
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ BigInteger n = new BigInteger("1569275433846670190958947355803350458831205595451630533029");
+ BigInteger h = BigInteger.valueOf(2);
+
+ ECCurve.F2m curve = new ECCurve.F2m(
+ 191, // m
+ 9, //k
+ new BigInteger("2866537B676752636A68F56554E12640276B649EF7526267", 16), // a
+ new BigInteger("2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC", 16), // b
+ n, h);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0436B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D765BE73433B3F95E332932E70EA245CA2418EA0EF98018FB")), // G
+ n, h);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("1275552191113212300012030439187146164646146646466749494799"), // d
+ params);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("045DE37E756BD55D72E3768CB396FFEB962614DEA4CE28A2E755C0E0E02F5FB132CAF416EF85B229BBB8E1352003125BA1")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+
+ /**
+ * X9.62 - 1998,<br>
+ * J.2.1, Page 100, ECDSA over the field F2m<br>
+ * an example with 191 bit binary field
+ */
+ private void testECDSA239bitBinary()
+ {
+ BigInteger r = new BigInteger("21596333210419611985018340039034612628818151486841789642455876922391552");
+ BigInteger s = new BigInteger("197030374000731686738334997654997227052849804072198819102649413465737174");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("171278725565216523967285789236956265265265235675811949404040041670216363"));
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ BigInteger n = new BigInteger("220855883097298041197912187592864814557886993776713230936715041207411783");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve.F2m curve = new ECCurve.F2m(
+ 239, // m
+ 36, //k
+ new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), // a
+ new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16), // b
+ n, h);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0457927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305")), // G
+ n, h);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("145642755521911534651321230007534120304391871461646461466464667494947990"), // d
+ params);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("045894609CCECF9A92533F630DE713A958E96C97CCB8F5ABB5A688A238DEED6DC2D9D0C94EBFB7D526BA6A61764175B99CB6011E2047F9F067293F57F5")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ // L 4.1 X9.62 2005
+ private void testECDSAP224sha224()
+ {
+ X9ECParameters p = NISTNamedCurves.getByName("P-224");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("6081831502424510080126737029209236539191290354021104541805484120491"), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(BigIntegers.asUnsignedByteArray(new BigInteger("15456715103636396133226117016818339719732885723579037388121116732601")));
+
+ byte[] M = Hex.decode("8797A3C693CC292441039A4E6BAB7387F3B4F2A63D00ED384B378C79");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("26477406756127720855365980332052585411804331993436302005017227573742");
+ BigInteger s = new BigInteger("17694958233103667059888193972742186995283044672015112738919822429978");
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("03FD44EC11F9D43D9D23B1E1D1C9ED6519B40ECF0C79F48CF476CC43F1")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ private void testECDSASecP224k1sha256()
+ {
+ X9ECParameters p = SECNamedCurves.getByName("secp224k1");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("BE6F6E91FE96840A6518B56F3FE21689903A64FA729057AB872A9F51", 16), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(Hex.decode("00c39beac93db21c3266084429eb9b846b787c094f23a4de66447efbb3"));
+
+ byte[] M = Hex.decode("E5D5A7ADF73C5476FAEE93A2C76CE94DC0557DB04CDC189504779117920B896D");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("8163E5941BED41DA441B33E653C632A55A110893133351E20CE7CB75", 16);
+ BigInteger s = new BigInteger("D12C3FC289DDD5F6890DCE26B65792C8C50E68BF551D617D47DF15A8", 16);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("04C5C9B38D3603FCCD6994CBB9594E152B658721E483669BB42728520F484B537647EC816E58A8284D3B89DFEDB173AFDC214ECA95A836FA7C")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ // L4.2 X9.62 2005
+ private void testECDSAP256sha256()
+ {
+ X9ECParameters p = NISTNamedCurves.getByName("P-256");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("20186677036482506117540275567393538695075300175221296989956723148347484984008"), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(BigIntegers.asUnsignedByteArray(new BigInteger("72546832179840998877302529996971396893172522460793442785601695562409154906335")));
+
+ byte[] M = Hex.decode("1BD4ED430B0F384B4E8D458EFF1A8A553286D7AC21CB2F6806172EF5F94A06AD");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("97354732615802252173078420023658453040116611318111190383344590814578738210384");
+ BigInteger s = new BigInteger("98506158880355671805367324764306888225238061309262649376965428126566081727535");
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("03596375E6CE57E0F20294FC46BDFCFD19A39F8161B58695B3EC5B3D16427C274D")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ private void testECDSAP224OneByteOver()
+ {
+ X9ECParameters p = NISTNamedCurves.getByName("P-224");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("6081831502424510080126737029209236539191290354021104541805484120491"), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(BigIntegers.asUnsignedByteArray(new BigInteger("15456715103636396133226117016818339719732885723579037388121116732601")));
+
+ byte[] M = Hex.decode("8797A3C693CC292441039A4E6BAB7387F3B4F2A63D00ED384B378C79FF");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("26477406756127720855365980332052585411804331993436302005017227573742");
+ BigInteger s = new BigInteger("17694958233103667059888193972742186995283044672015112738919822429978");
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("03FD44EC11F9D43D9D23B1E1D1C9ED6519B40ECF0C79F48CF476CC43F1")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ // L4.3 X9.62 2005
+ private void testECDSAP521sha512()
+ {
+ X9ECParameters p = NISTNamedCurves.getByName("P-521");
+ ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH());
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("617573726813476282316253885608633222275541026607493641741273231656161177732180358888434629562647985511298272498852936680947729040673640492310550142822667389"), // d
+ params);
+ SecureRandom k = new FixedSecureRandom(BigIntegers.asUnsignedByteArray(new BigInteger("6806532878215503520845109818432174847616958675335397773700324097584974639728725689481598054743894544060040710846048585856076812050552869216017728862957612913")));
+
+ byte[] M = Hex.decode("6893B64BD3A9615C39C3E62DDD269C2BAAF1D85915526083183CE14C2E883B48B193607C1ED871852C9DF9C3147B574DC1526C55DE1FE263A676346A20028A66");
+
+ ECDSASigner dsa = new ECDSASigner();
+
+ dsa.init(true, new ParametersWithRandom(priKey, k));
+
+ BigInteger[] sig = dsa.generateSignature(M);
+
+ BigInteger r = new BigInteger("1368926195812127407956140744722257403535864168182534321188553460365652865686040549247096155740756318290773648848859639978618869784291633651685766829574104630");
+ BigInteger s = new BigInteger("1624754720348883715608122151214003032398685415003935734485445999065609979304811509538477657407457976246218976767156629169821116579317401249024208611945405790");
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ params.getCurve().decodePoint(Hex.decode("020145E221AB9F71C5FE740D8D2B94939A09E2816E2167A7D058125A06A80C014F553E8D6764B048FB6F2B687CEC72F39738F223D4CE6AFCBFF2E34774AA5D3C342CB3")), // Q
+ params);
+
+ dsa.init(false, pubKey);
+ if (!dsa.verifySignature(M, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * General test for long digest.
+ */
+ private void testECDSA239bitBinaryAndLargeDigest()
+ {
+ BigInteger r = new BigInteger("21596333210419611985018340039034612628818151486841789642455876922391552");
+ BigInteger s = new BigInteger("144940322424411242416373536877786566515839911620497068645600824084578597");
+
+ byte[] kData = BigIntegers.asUnsignedByteArray(new BigInteger("171278725565216523967285789236956265265265235675811949404040041670216363"));
+
+ SecureRandom k = new FixedSecureRandom(kData);
+
+ BigInteger n = new BigInteger("220855883097298041197912187592864814557886993776713230936715041207411783");
+ BigInteger h = BigInteger.valueOf(4);
+
+ ECCurve.F2m curve = new ECCurve.F2m(
+ 239, // m
+ 36, //k
+ new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), // a
+ new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16), // b
+ n, h);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("0457927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305")), // G
+ n, h);
+
+ ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(
+ new BigInteger("145642755521911534651321230007534120304391871461646461466464667494947990"), // d
+ params);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+ ParametersWithRandom param = new ParametersWithRandom(priKey, k);
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517968236873715988614170569073515315707566766479517968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ if (!r.equals(sig[0]))
+ {
+ fail("r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r + System.getProperty("line.separator")
+ + " got : " + sig[0]);
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ fail("s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s + System.getProperty("line.separator")
+ + " got : " + sig[1]);
+ }
+
+ // Verify the signature
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
+ curve.decodePoint(Hex.decode("045894609CCECF9A92533F630DE713A958E96C97CCB8F5ABB5A688A238DEED6DC2D9D0C94EBFB7D526BA6A61764175B99CB6011E2047F9F067293F57F5")), // Q
+ params);
+
+ ecdsa.init(false, pubKey);
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * key generation test
+ */
+ private void testECDSAKeyGenTest()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger n = new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ n);
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ ECDSASigner ecdsa = new ECDSASigner();
+
+ ecdsa.init(true, param);
+
+ byte[] message = new BigInteger("968236873715988614170569073515315707566766479517").toByteArray();
+ BigInteger[] sig = ecdsa.generateSignature(message);
+
+ ecdsa.init(false, pair.getPublic());
+
+ if (!ecdsa.verifySignature(message, sig[0], sig[1]))
+ {
+ fail("signature fails");
+ }
+ }
+
+ /**
+ * Basic Key Agreement Test
+ */
+ private void testECBasicAgreementTest()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger n = new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters params = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ n);
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+ ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
+ params,
+ random);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair p1 = pGen.generateKeyPair();
+ AsymmetricCipherKeyPair p2 = pGen.generateKeyPair();
+
+ //
+ // two way
+ //
+ BasicAgreement e1 = new ECDHBasicAgreement();
+ BasicAgreement e2 = new ECDHBasicAgreement();
+
+ e1.init(p1.getPrivate());
+ e2.init(p2.getPrivate());
+
+ BigInteger k1 = e1.calculateAgreement(p2.getPublic());
+ BigInteger k2 = e2.calculateAgreement(p1.getPublic());
+
+ if (!k1.equals(k2))
+ {
+ fail("calculated agreement test failed");
+ }
+
+ //
+ // two way
+ //
+ e1 = new ECDHCBasicAgreement();
+ e2 = new ECDHCBasicAgreement();
+
+ e1.init(p1.getPrivate());
+ e2.init(p2.getPrivate());
+
+ k1 = e1.calculateAgreement(p2.getPublic());
+ k2 = e2.calculateAgreement(p1.getPublic());
+
+ if (!k1.equals(k2))
+ {
+ fail("calculated agreement test failed");
+ }
+ }
+
+ private void testECMQVTestVector1()
+ {
+ // Test Vector from GEC-2
+
+ X9ECParameters x9 = SECNamedCurves.getByName("secp160r1");
+ ECDomainParameters p = new ECDomainParameters(
+ x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+ AsymmetricCipherKeyPair U1 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("0251B4496FECC406ED0E75A24A3C03206251419DC0")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("AA374FFC3CE144E6B073307972CB6D57B2A4E982", 16), p));
+
+ AsymmetricCipherKeyPair U2 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("03D99CE4D8BF52FA20BD21A962C6556B0F71F4CA1F")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("149EC7EA3A220A887619B3F9E5B4CA51C7D1779C", 16), p));
+
+ AsymmetricCipherKeyPair V1 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("0349B41E0E9C0369C2328739D90F63D56707C6E5BC")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("45FB58A92A17AD4B15101C66E74F277E2B460866", 16), p));
+
+ AsymmetricCipherKeyPair V2 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("02706E5D6E1F640C6E9C804E75DBC14521B1E5F3B5")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("18C13FCED9EADF884F7C595C8CB565DEFD0CB41E", 16), p));
+
+ BigInteger x = calculateAgreement(U1, U2, V1, V2);
+
+ if (x == null
+ || !x.equals(new BigInteger("5A6955CEFDB4E43255FB7FCF718611E4DF8E05AC", 16)))
+ {
+ fail("MQV Test Vector #1 agreement failed");
+ }
+ }
+
+ private void testECMQVTestVector2()
+ {
+ // Test Vector from GEC-2
+
+ X9ECParameters x9 = SECNamedCurves.getByName("sect163k1");
+ ECDomainParameters p = new ECDomainParameters(
+ x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
+
+ AsymmetricCipherKeyPair U1 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("03037D529FA37E42195F10111127FFB2BB38644806BC")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("03A41434AA99C2EF40C8495B2ED9739CB2155A1E0D", 16), p));
+
+ AsymmetricCipherKeyPair U2 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("02015198E74BC2F1E5C9A62B80248DF0D62B9ADF8429")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("032FC4C61A8211E6A7C4B8B0C03CF35F7CF20DBD52", 16), p));
+
+ AsymmetricCipherKeyPair V1 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("03072783FAAB9549002B4F13140B88132D1C75B3886C")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("57E8A78E842BF4ACD5C315AA0569DB1703541D96", 16), p));
+
+ AsymmetricCipherKeyPair V2 = new AsymmetricCipherKeyPair(
+ new ECPublicKeyParameters(
+ p.getCurve().decodePoint(Hex.decode("03067E3AEA3510D69E8EDD19CB2A703DDC6CF5E56E32")), p),
+ new ECPrivateKeyParameters(
+ new BigInteger("02BD198B83A667A8D908EA1E6F90FD5C6D695DE94F", 16), p));
+
+ BigInteger x = calculateAgreement(U1, U2, V1, V2);
+
+ if (x == null
+ || !x.equals(new BigInteger("038359FFD30C0D5FC1E6154F483B73D43E5CF2B503", 16)))
+ {
+ fail("MQV Test Vector #2 agreement failed");
+ }
+ }
+
+ private void testECMQVRandom()
+ {
+ SecureRandom random = new SecureRandom();
+
+ BigInteger n = new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307");
+
+ ECCurve.Fp curve = new ECCurve.Fp(
+ new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
+ new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
+ new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16), // b
+ n, ECConstants.ONE);
+
+ ECDomainParameters parameters = new ECDomainParameters(
+ curve,
+ curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
+ n);
+
+ ECKeyPairGenerator pGen = new ECKeyPairGenerator();
+
+ pGen.init(new ECKeyGenerationParameters(parameters, random));
+
+
+ // Pre-established key pairs
+ AsymmetricCipherKeyPair U1 = pGen.generateKeyPair();
+ AsymmetricCipherKeyPair V1 = pGen.generateKeyPair();
+
+ // Ephemeral key pairs
+ AsymmetricCipherKeyPair U2 = pGen.generateKeyPair();
+ AsymmetricCipherKeyPair V2 = pGen.generateKeyPair();
+
+ BigInteger x = calculateAgreement(U1, U2, V1, V2);
+
+ if (x == null)
+ {
+ fail("MQV Test Vector (random) agreement failed");
+ }
+ }
+
+ private static BigInteger calculateAgreement(
+ AsymmetricCipherKeyPair U1,
+ AsymmetricCipherKeyPair U2,
+ AsymmetricCipherKeyPair V1,
+ AsymmetricCipherKeyPair V2)
+ {
+ ECMQVBasicAgreement u = new ECMQVBasicAgreement();
+ u.init(new MQVPrivateParameters(
+ (ECPrivateKeyParameters)U1.getPrivate(),
+ (ECPrivateKeyParameters)U2.getPrivate(),
+ (ECPublicKeyParameters)U2.getPublic()));
+ BigInteger ux = u.calculateAgreement(new MQVPublicParameters(
+ (ECPublicKeyParameters)V1.getPublic(),
+ (ECPublicKeyParameters)V2.getPublic()));
+
+ ECMQVBasicAgreement v = new ECMQVBasicAgreement();
+ v.init(new MQVPrivateParameters(
+ (ECPrivateKeyParameters)V1.getPrivate(),
+ (ECPrivateKeyParameters)V2.getPrivate(),
+ (ECPublicKeyParameters)V2.getPublic()));
+ BigInteger vx = v.calculateAgreement(new MQVPublicParameters(
+ (ECPublicKeyParameters)U1.getPublic(),
+ (ECPublicKeyParameters)U2.getPublic()));
+
+ if (ux.equals(vx))
+ {
+ return ux;
+ }
+
+ return null;
+ }
+
+ public String getName()
+ {
+ return "EC";
+ }
+
+ public void performTest()
+ {
+ decodeTest();
+ testECDSA192bitPrime();
+ testECDSA239bitPrime();
+ testECDSA191bitBinary();
+ testECDSA239bitBinary();
+ testECDSAKeyGenTest();
+ testECBasicAgreementTest();
+
+ testECDSAP224sha224();
+ testECDSAP224OneByteOver();
+ testECDSAP256sha256();
+ testECDSAP521sha512();
+ testECDSASecP224k1sha256();
+ testECDSA239bitBinaryAndLargeDigest();
+
+ testECMQVTestVector1();
+ testECMQVTestVector2();
+ testECMQVRandom();
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ECTest());
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ElGamalTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ElGamalTest.java
new file mode 100644
index 00000000..fb936993
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ElGamalTest.java
@@ -0,0 +1,285 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.engines.ElGamalEngine;
+import org.bouncycastle.crypto.generators.ElGamalKeyPairGenerator;
+import org.bouncycastle.crypto.generators.ElGamalParametersGenerator;
+import org.bouncycastle.crypto.params.ElGamalKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ElGamalParameters;
+import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class ElGamalTest
+ extends SimpleTest
+{
+ private BigInteger g512 = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16);
+ private BigInteger p512 = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16);
+
+ private BigInteger g768 = new BigInteger("7c240073c1316c621df461b71ebb0cdcc90a6e5527e5e126633d131f87461c4dc4afc60c2cb0f053b6758871489a69613e2a8b4c8acde23954c08c81cbd36132cfd64d69e4ed9f8e51ed6e516297206672d5c0a69135df0a5dcf010d289a9ca1", 16);
+ private BigInteger p768 = new BigInteger("8c9dd223debed1b80103b8b309715be009d48860ed5ae9b9d5d8159508efd802e3ad4501a7f7e1cfec78844489148cd72da24b21eddd01aa624291c48393e277cfc529e37075eccef957f3616f962d15b44aeab4039d01b817fde9eaa12fd73f", 16);
+
+ private BigInteger g1024 = new BigInteger("1db17639cdf96bc4eabba19454f0b7e5bd4e14862889a725c96eb61048dcd676ceb303d586e30f060dbafd8a571a39c4d823982117da5cc4e0f89c77388b7a08896362429b94a18a327604eb7ff227bffbc83459ade299e57b5f77b50fb045250934938efa145511166e3197373e1b5b1e52de713eb49792bedde722c6717abf", 16);
+ private BigInteger p1024 = new BigInteger("a00e283b3c624e5b2b4d9fbc2653b5185d99499b00fd1bf244c6f0bb817b4d1c451b2958d62a0f8a38caef059fb5ecd25d75ed9af403f5b5bdab97a642902f824e3c13789fed95fa106ddfe0ff4a707c85e2eb77d49e68f2808bcea18ce128b178cd287c6bc00efa9a1ad2a673fe0dceace53166f75b81d6709d5f8af7c66bb7", 16);
+
+ private BigInteger yPgpBogusPSamp = new BigInteger("de4688497cc05b45fe8559bc9918c45afcad69b74123a7236eba409fd9de8ea34c7869839ee9df35e3d97576145d089841aa65b5b4e061fae52c37e430354269a02496b8ed8456f2d0d7c9b0db985fbcb21ae9f78507ed6e3a29db595b201b1a4f931c7d791eede65ccf918e8a61cf146859151c78c41ad48853694623467d78", 16);
+ private BigInteger xPgpBogusPSamp = new BigInteger("cbaf780f2cfe4f987bbc5fcb0738bbd7912060ccfdf37cbfeea65c0fd857e74a8df6cc359375f28cf5725d081813c614410a78cbe4b06d677beea9ff0fa10b1dbc47a6ed8c5b8466d6a95d6574029dbdf72596392e1b6b230faf9916dc8455821c10527a375a4d1c8a54947d1fe714d321aca25ad486b4b456506999fd2fd11a", 16);
+ private BigInteger gPgpBogusPSamp = new BigInteger("153ffe9522076d1cbd6e75f0816a0fc2ebd8b0e0091406587387a1763022088a03b411eed07ff50efb82b21f1608c352d10f63ba7e7e981a2f3387cec8af2915953d00493857663ae8919f517fe90f1d2abe7af4305a344b10d1a25d75f65902cd7fd775853d3ac43d7c5253ad666e1e63ee98cdcb10af81273d4ff053ff07d51", 16);
+ private BigInteger pPgpBogusPSamp = new BigInteger("15061b26cdab4e865098a01c86f13b03220104c5443e950658b36b85245aa0c616a0c0d8d99c454bea087c172315e45b3bc9b925443948a2b6ba47608a6035b9a79a4ef34a78d7274a12ede8364f02d5030db864988643d7e92753df603bd69fbd2682ab0af64d1a866d1131a2cb13333cedb0a9e6eefddd9fff8154d34c2daab", 16);
+ private int lPgpBogusPSamp = 0;
+
+ public String getName()
+ {
+ return "ElGamal";
+ }
+
+ private void testEnc(
+ int size,
+ int privateValueSize,
+ BigInteger g,
+ BigInteger p)
+ {
+ ElGamalParameters dhParams = new ElGamalParameters(p, g, privateValueSize);
+ ElGamalKeyGenerationParameters params = new ElGamalKeyGenerationParameters(new SecureRandom(), dhParams);
+ ElGamalKeyPairGenerator kpGen = new ElGamalKeyPairGenerator();
+
+ kpGen.init(params);
+
+ //
+ // generate pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ ElGamalPublicKeyParameters pu = (ElGamalPublicKeyParameters)pair.getPublic();
+ ElGamalPrivateKeyParameters pv = (ElGamalPrivateKeyParameters)pair.getPrivate();
+
+ checkKeySize(privateValueSize, pv);
+
+ ElGamalEngine e = new ElGamalEngine();
+
+ e.init(true, pu);
+
+ if (e.getOutputBlockSize() != size / 4)
+ {
+ fail(size + " getOutputBlockSize() on encryption failed.");
+ }
+
+ byte[] message = Hex.decode("5468697320697320612074657374");
+
+ byte[] pText = message;
+ byte[] cText = e.processBlock(pText, 0, pText.length);
+
+ e.init(false, pv);
+
+ if (e.getOutputBlockSize() != (size / 8) - 1)
+ {
+ fail(size + " getOutputBlockSize() on decryption failed.");
+ }
+
+ pText = e.processBlock(cText, 0, cText.length);
+
+ if (!Arrays.areEqual(message, pText))
+ {
+ fail(size + " bit test failed");
+ }
+
+ e.init(true, pu);
+
+ byte[] bytes = new byte[e.getInputBlockSize() + 2];
+
+ try
+ {
+ e.processBlock(bytes, 0, bytes.length);
+
+ fail("out of range block not detected");
+ }
+ catch (DataLengthException ex)
+ {
+ // expected
+ }
+
+ try
+ {
+ bytes[0] = (byte)0xff;
+
+ e.processBlock(bytes, 0, bytes.length - 1);
+
+ fail("out of range block not detected");
+ }
+ catch (DataLengthException ex)
+ {
+ // expected
+ }
+
+ try
+ {
+ bytes[0] = (byte)0x7f;
+
+ e.processBlock(bytes, 0, bytes.length - 1);
+ }
+ catch (DataLengthException ex)
+ {
+ fail("in range block failed");
+ }
+
+ try
+ {
+ bytes = BigIntegers.asUnsignedByteArray(p);
+
+ e.processBlock(bytes, 0, bytes.length);
+
+ fail("out of range block not detected");
+ }
+ catch (DataLengthException ex)
+ {
+ // expected
+ }
+
+ try
+ {
+ bytes = BigIntegers.asUnsignedByteArray(p.subtract(BigInteger.valueOf(1)));
+
+ e.processBlock(bytes, 0, bytes.length);
+ }
+ catch (DataLengthException ex)
+ {
+ fail("boundary block rejected");
+ }
+ }
+
+ private void checkKeySize(
+ int privateValueSize,
+ ElGamalPrivateKeyParameters priv)
+ {
+ if (privateValueSize != 0)
+ {
+ if (priv.getX().bitLength() != privateValueSize)
+ {
+ fail("limited key check failed for key size " + privateValueSize);
+ }
+ }
+ }
+
+ /**
+ * this test is can take quiet a while
+ *
+ * @param size size of key in bits.
+ */
+ private void testGeneration(
+ int size)
+ {
+ ElGamalParametersGenerator pGen = new ElGamalParametersGenerator();
+
+ pGen.init(size, 10, new SecureRandom());
+
+ ElGamalParameters elParams = pGen.generateParameters();
+
+ if (elParams.getL() != 0)
+ {
+ fail("ElGamalParametersGenerator failed to set L to 0 in generated ElGamalParameters");
+ }
+
+ ElGamalKeyGenerationParameters params = new ElGamalKeyGenerationParameters(new SecureRandom(), elParams);
+
+ ElGamalKeyPairGenerator kpGen = new ElGamalKeyPairGenerator();
+
+ kpGen.init(params);
+
+ //
+ // generate first pair
+ //
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+
+ ElGamalPublicKeyParameters pu = (ElGamalPublicKeyParameters)pair.getPublic();
+ ElGamalPrivateKeyParameters pv = (ElGamalPrivateKeyParameters)pair.getPrivate();
+
+ ElGamalEngine e = new ElGamalEngine();
+
+ e.init(true, new ParametersWithRandom(pu, new SecureRandom()));
+
+ byte[] message = Hex.decode("5468697320697320612074657374");
+
+ byte[] pText = message;
+ byte[] cText = e.processBlock(pText, 0, pText.length);
+
+ e.init(false, pv);
+
+ pText = e.processBlock(cText, 0, cText.length);
+
+ if (!Arrays.areEqual(message, pText))
+ {
+ fail("generation test failed");
+ }
+ }
+
+ private void testInitCheck()
+ {
+ try
+ {
+ new ElGamalEngine().processBlock(new byte[]{ 1 }, 0, 1);
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ }
+
+ private void testInvalidP()
+ {
+ ElGamalParameters dhParams = new ElGamalParameters(pPgpBogusPSamp, gPgpBogusPSamp, lPgpBogusPSamp);
+ ElGamalPublicKeyParameters pu = new ElGamalPublicKeyParameters(yPgpBogusPSamp, dhParams);
+ ElGamalPrivateKeyParameters pv = new ElGamalPrivateKeyParameters(xPgpBogusPSamp, dhParams);
+
+ ElGamalEngine e = new ElGamalEngine();
+
+ e.init(true, pu);
+
+ byte[] message = Hex.decode("5468697320697320612074657374");
+
+ byte[] pText = message;
+ byte[] cText = e.processBlock(pText, 0, pText.length);
+
+ e.init(false, pv);
+
+ pText = e.processBlock(cText, 0, cText.length);
+
+ if (Arrays.areEqual(message, pText))
+ {
+ fail("invalid test failed");
+ }
+ }
+
+ public void performTest()
+ {
+ testInvalidP();
+
+ testEnc(512, 0, g512, p512);
+ testEnc(768, 0, g768, p768);
+ testEnc(1024, 0, g1024, p1024);
+
+ testEnc(512, 64, g512, p512);
+ testEnc(768, 128, g768, p768);
+
+ //
+ // generation test.
+ //
+ testGeneration(258);
+
+ testInitCheck();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ElGamalTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/EqualsHashCodeTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/EqualsHashCodeTest.java
new file mode 100644
index 00000000..1ab7cc49
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/EqualsHashCodeTest.java
@@ -0,0 +1,261 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
+import org.bouncycastle.crypto.generators.ElGamalKeyPairGenerator;
+import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
+import org.bouncycastle.crypto.params.DHKeyParameters;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.DHValidationParameters;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAValidationParameters;
+import org.bouncycastle.crypto.params.ElGamalKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ElGamalKeyParameters;
+import org.bouncycastle.crypto.params.ElGamalParameters;
+import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
+import org.bouncycastle.crypto.params.GOST3410Parameters;
+import org.bouncycastle.crypto.params.GOST3410ValidationParameters;
+import org.bouncycastle.util.test.SimpleTest;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+class DHTestKeyParameters
+ extends DHKeyParameters
+{
+ protected DHTestKeyParameters(boolean isPrivate, DHParameters params)
+ {
+ super(isPrivate, params);
+ }
+}
+
+class ElGamalTestKeyParameters
+ extends ElGamalKeyParameters
+{
+ protected ElGamalTestKeyParameters(boolean isPrivate, ElGamalParameters params)
+ {
+ super(isPrivate, params);
+ }
+}
+
+public class EqualsHashCodeTest
+ extends SimpleTest
+{
+ private static Object OTHER = new Object();
+
+ public String getName()
+ {
+ return "EqualsHashCode";
+ }
+
+ private void doTest(Object a, Object equalsA, Object notEqualsA)
+ {
+ if (a.equals(null))
+ {
+ fail("a equaled null");
+ }
+
+ if (!a.equals(equalsA) || !equalsA.equals(a))
+ {
+ fail("equality failed");
+ }
+
+ if (a.equals(OTHER))
+ {
+ fail("other inequality failed");
+ }
+
+ if (a.equals(notEqualsA) || notEqualsA.equals(a))
+ {
+ fail("inequality failed");
+ }
+
+ if (a.hashCode() != equalsA.hashCode())
+ {
+ fail("hashCode equality failed");
+ }
+ }
+
+ private void dhTest()
+ {
+ BigInteger g512 = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16);
+ BigInteger p512 = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16);
+
+ DHParameters dhParams = new DHParameters(p512, g512);
+ DHKeyGenerationParameters params = new DHKeyGenerationParameters(new SecureRandom(), dhParams); DHKeyPairGenerator kpGen = new DHKeyPairGenerator();
+
+ kpGen.init(params);
+
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+ DHPublicKeyParameters pu1 = (DHPublicKeyParameters)pair.getPublic();
+ DHPrivateKeyParameters pv1 = (DHPrivateKeyParameters)pair.getPrivate();
+
+ DHPublicKeyParameters pu2 = new DHPublicKeyParameters(pu1.getY(), pu1.getParameters());
+ DHPrivateKeyParameters pv2 = new DHPrivateKeyParameters(pv1.getX(), pv1.getParameters());
+ DHPublicKeyParameters pu3 = new DHPublicKeyParameters(pv1.getX(), pu1.getParameters());
+ DHPrivateKeyParameters pv3 = new DHPrivateKeyParameters(pu1.getY(), pu1.getParameters());
+
+ doTest(pu1, pu2, pu3);
+ doTest(pv1, pv2, pv3);
+
+ DHParameters pr1 = pu1.getParameters();
+ DHParameters pr2 = new DHParameters(pr1.getP(), pr1.getG(), pr1.getQ(), pr1.getM(), pr1.getL(), pr1.getJ(), pr1.getValidationParameters());
+ DHParameters pr3 = new DHParameters(pr1.getG(), pr1.getP(), pr1.getQ(), pr1.getM(), pr1.getL(), pr1.getJ(), pr1.getValidationParameters());
+
+ doTest(pr1, pr2, pr3);
+
+ pr3 = new DHParameters(pr1.getG(), pr1.getP(), null, pr1.getM(), pr1.getL(), pr1.getJ(), pr1.getValidationParameters());
+
+ doTest(pr1, pr2, pr3);
+
+ pu2 = new DHPublicKeyParameters(pu1.getY(), pr2);
+ pv2 = new DHPrivateKeyParameters(pv1.getX(), pr2);
+
+ doTest(pu1, pu2, pu3);
+ doTest(pv1, pv2, pv3);
+
+ DHValidationParameters vp1 = new DHValidationParameters(new byte[20], 1024);
+ DHValidationParameters vp2 = new DHValidationParameters(new byte[20], 1024);
+ DHValidationParameters vp3 = new DHValidationParameters(new byte[24], 1024);
+
+ doTest(vp1, vp1, vp3);
+ doTest(vp1, vp2, vp3);
+
+ byte[] bytes = new byte[20];
+ bytes[0] = 1;
+
+ vp3 = new DHValidationParameters(bytes, 1024);
+
+ doTest(vp1, vp2, vp3);
+
+ vp3 = new DHValidationParameters(new byte[20], 2048);
+
+ doTest(vp1, vp2, vp3);
+
+ DHTestKeyParameters k1 = new DHTestKeyParameters(false, null);
+ DHTestKeyParameters k2 = new DHTestKeyParameters(false, null);
+ DHTestKeyParameters k3 = new DHTestKeyParameters(false, pu1.getParameters());
+
+ doTest(k1, k2, k3);
+ }
+
+ private void elGamalTest()
+ {
+ BigInteger g512 = new BigInteger("153d5d6172adb43045b68ae8e1de1070b6137005686d29d3d73a7749199681ee5b212c9b96bfdcfa5b20cd5e3fd2044895d609cf9b410b7a0f12ca1cb9a428cc", 16);
+ BigInteger p512 = new BigInteger("9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e31db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b", 16);
+
+ ElGamalParameters dhParams = new ElGamalParameters(p512, g512);
+ ElGamalKeyGenerationParameters params = new ElGamalKeyGenerationParameters(new SecureRandom(), dhParams); ElGamalKeyPairGenerator kpGen = new ElGamalKeyPairGenerator();
+
+ kpGen.init(params);
+
+ AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
+ ElGamalPublicKeyParameters pu1 = (ElGamalPublicKeyParameters)pair.getPublic();
+ ElGamalPrivateKeyParameters pv1 = (ElGamalPrivateKeyParameters)pair.getPrivate();
+
+ ElGamalPublicKeyParameters pu2 = new ElGamalPublicKeyParameters(pu1.getY(), pu1.getParameters());
+ ElGamalPrivateKeyParameters pv2 = new ElGamalPrivateKeyParameters(pv1.getX(), pv1.getParameters());
+ ElGamalPublicKeyParameters pu3 = new ElGamalPublicKeyParameters(pv1.getX(), pu1.getParameters());
+ ElGamalPrivateKeyParameters pv3 = new ElGamalPrivateKeyParameters(pu1.getY(), pu1.getParameters());
+
+ doTest(pu1, pu2, pu3);
+ doTest(pv1, pv2, pv3);
+
+ ElGamalParameters pr1 = pu1.getParameters();
+ ElGamalParameters pr2 = new ElGamalParameters(pr1.getP(), pr1.getG());
+ ElGamalParameters pr3 = new ElGamalParameters(pr1.getG(), pr1.getP());
+
+ doTest(pr1, pr2, pr3);
+
+ pu2 = new ElGamalPublicKeyParameters(pu1.getY(), pr2);
+ pv2 = new ElGamalPrivateKeyParameters(pv1.getX(), pr2);
+
+ doTest(pu1, pu2, pu3);
+ doTest(pv1, pv2, pv3);
+
+ ElGamalTestKeyParameters k1 = new ElGamalTestKeyParameters(false, null);
+ ElGamalTestKeyParameters k2 = new ElGamalTestKeyParameters(false, null);
+ ElGamalTestKeyParameters k3 = new ElGamalTestKeyParameters(false, pu1.getParameters());
+
+ doTest(k1, k2, k3);
+ }
+
+ private void dsaTest()
+ {
+ BigInteger a = BigInteger.valueOf(1), b = BigInteger.valueOf(2), c = BigInteger.valueOf(3);
+
+ DSAParameters dsaP1 = new DSAParameters(a, b, c);
+ DSAParameters dsaP2 = new DSAParameters(a, b, c);
+ DSAParameters dsaP3 = new DSAParameters(b, c, a);
+
+ doTest(dsaP1, dsaP2, dsaP3);
+
+ DSAValidationParameters vp1 = new DSAValidationParameters(new byte[20], 1024);
+ DSAValidationParameters vp2 = new DSAValidationParameters(new byte[20], 1024);
+ DSAValidationParameters vp3 = new DSAValidationParameters(new byte[24], 1024);
+
+ doTest(vp1, vp1, vp3);
+ doTest(vp1, vp2, vp3);
+
+ byte[] bytes = new byte[20];
+ bytes[0] = 1;
+
+ vp3 = new DSAValidationParameters(bytes, 1024);
+
+ doTest(vp1, vp2, vp3);
+
+ vp3 = new DSAValidationParameters(new byte[20], 2048);
+
+ doTest(vp1, vp2, vp3);
+ }
+
+ private void gost3410Test()
+ {
+ BigInteger a = BigInteger.valueOf(1), b = BigInteger.valueOf(2), c = BigInteger.valueOf(3);
+
+ GOST3410Parameters g1 = new GOST3410Parameters(a, b, c);
+ GOST3410Parameters g2 = new GOST3410Parameters(a, b, c);
+ GOST3410Parameters g3 = new GOST3410Parameters(a, c, c);
+
+ doTest(g1, g2, g3);
+
+ GOST3410ValidationParameters v1 = new GOST3410ValidationParameters(100, 1);
+ GOST3410ValidationParameters v2 = new GOST3410ValidationParameters(100, 1);
+ GOST3410ValidationParameters v3 = new GOST3410ValidationParameters(101, 1);
+
+ doTest(v1, v2, v3);
+
+ v3 = new GOST3410ValidationParameters(100, 2);
+
+ doTest(v1, v2, v3);
+
+ v1 = new GOST3410ValidationParameters(100L, 1L);
+ v2 = new GOST3410ValidationParameters(100L, 1L);
+ v3 = new GOST3410ValidationParameters(101L, 1L);
+
+ doTest(v1, v2, v3);
+
+ v3 = new GOST3410ValidationParameters(100L, 2L);
+
+ doTest(v1, v2, v3);
+
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ dhTest();
+ elGamalTest();
+ gost3410Test();
+ dsaTest();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new EqualsHashCodeTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMReorderTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMReorderTest.java
new file mode 100644
index 00000000..efac338f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMReorderTest.java
@@ -0,0 +1,347 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.bouncycastle.crypto.modes.gcm.GCMExponentiator;
+import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
+import org.bouncycastle.crypto.modes.gcm.Tables64kGCMMultiplier;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+import org.bouncycastle.util.encoders.Hex;
+
+public class GCMReorderTest
+ extends TestCase
+{
+ private static final byte[] H;
+ private static final SecureRandom random = new SecureRandom();
+ private static final GCMMultiplier mul = new Tables64kGCMMultiplier();
+ private static final GCMExponentiator exp = new Tables1kGCMExponentiator();
+ private static final byte[] EMPTY = new byte[0];
+
+ static
+ {
+ H = new byte[16];
+ random.nextBytes(H);
+ mul.init(Arrays.clone(H));
+ exp.init(Arrays.clone(H));
+ }
+
+ public void testCombine() throws Exception
+ {
+ for (int count = 0; count < 10; ++count)
+ {
+ byte[] A = randomBytes(1000);
+ byte[] C = randomBytes(1000);
+
+ byte[] ghashA_ = GHASH(A, EMPTY);
+ byte[] ghash_C = GHASH(EMPTY, C);
+ byte[] ghashAC = GHASH(A, C);
+
+ byte[] ghashCombine = combine_GHASH(ghashA_, (long)A.length * 8, ghash_C, (long)C.length * 8);
+
+ assertTrue(Arrays.areEqual(ghashAC, ghashCombine));
+ }
+ }
+
+ public void testConcatAuth() throws Exception
+ {
+ for (int count = 0; count < 10; ++count)
+ {
+ byte[] P = randomBlocks(100);
+ byte[] A = randomBytes(1000);
+ byte[] PA = concatArrays(P, A);
+
+ byte[] ghashP_ = GHASH(P, EMPTY);
+ byte[] ghashA_ = GHASH(A, EMPTY);
+ byte[] ghashPA_ = GHASH(PA, EMPTY);
+ byte[] ghashConcat = concatAuth_GHASH(ghashP_, (long)P.length * 8, ghashA_, (long)A.length * 8);
+
+ assertTrue(Arrays.areEqual(ghashPA_, ghashConcat));
+ }
+ }
+
+ public void testConcatCrypt() throws Exception
+ {
+ for (int count = 0; count < 10; ++count)
+ {
+ byte[] P = randomBlocks(100);
+ byte[] A = randomBytes(1000);
+ byte[] PA = concatArrays(P, A);
+
+ byte[] ghash_P = GHASH(EMPTY, P);
+ byte[] ghash_A = GHASH(EMPTY, A);
+ byte[] ghash_PA = GHASH(EMPTY, PA);
+ byte[] ghashConcat = concatCrypt_GHASH(ghash_P, (long)P.length * 8, ghash_A, (long)A.length * 8);
+
+ assertTrue(Arrays.areEqual(ghash_PA, ghashConcat));
+ }
+ }
+
+ public void testExp()
+ {
+ {
+ byte[] buf1 = new byte[16];
+ buf1[0] = (byte)0x80;
+
+ byte[] buf2 = new byte[16];
+
+ for (int pow = 0; pow != 100; ++pow)
+ {
+ exp.exponentiateX(pow, buf2);
+
+ assertTrue(Arrays.areEqual(buf1, buf2));
+
+ mul.multiplyH(buf1);
+ }
+ }
+
+ long[] testPow = new long[]{ 10, 1, 8, 17, 24, 13, 2, 13, 2, 3 };
+ byte[][] testData = new byte[][]{
+ Hex.decode("9185848a877bd87ba071e281f476e8e7"),
+ Hex.decode("697ce3052137d80745d524474fb6b290"),
+ Hex.decode("2696fc47198bb23b11296e4f88720a17"),
+ Hex.decode("01f2f0ead011a4ae0cf3572f1b76dd8e"),
+ Hex.decode("a53060694a044e4b7fa1e661c5a7bb6b"),
+ Hex.decode("39c0392e8b6b0e04a7565c85394c2c4c"),
+ Hex.decode("519c362d502e07f2d8b7597a359a5214"),
+ Hex.decode("5a527a393675705e19b2117f67695af4"),
+ Hex.decode("27fc0901d1d332a53ba4d4386c2109d2"),
+ Hex.decode("93ca9b57174aabedf8220e83366d7df6"),
+ };
+
+ for (int i = 0; i != 10; ++i)
+ {
+ long pow = testPow[i];
+ byte[] data = Arrays.clone(testData[i]);
+
+ byte[] expected = Arrays.clone(data);
+ for (int j = 0; j < pow; ++j)
+ {
+ mul.multiplyH(expected);
+ }
+
+ byte[] H_a = new byte[16];
+ exp.exponentiateX(pow, H_a);
+ byte[] actual = multiply(data, H_a);
+
+ assertTrue(Arrays.areEqual(expected, actual));
+ }
+ }
+
+ public void testMultiply()
+ {
+ byte[] expected = Arrays.clone(H);
+ mul.multiplyH(expected);
+
+ assertTrue(Arrays.areEqual(expected, multiply(H, H)));
+
+ for (int count = 0; count < 10; ++count)
+ {
+ byte[] a = new byte[16];
+ random.nextBytes(a);
+
+ byte[] b = new byte[16];
+ random.nextBytes(b);
+
+ expected = Arrays.clone(a);
+ mul.multiplyH(expected);
+ assertTrue(Arrays.areEqual(expected, multiply(a, H)));
+ assertTrue(Arrays.areEqual(expected, multiply(H, a)));
+
+ expected = Arrays.clone(b);
+ mul.multiplyH(expected);
+ assertTrue(Arrays.areEqual(expected, multiply(b, H)));
+ assertTrue(Arrays.areEqual(expected, multiply(H, b)));
+
+ assertTrue(Arrays.areEqual(multiply(a, b), multiply(b, a)));
+ }
+ }
+
+ private byte[] randomBlocks(int upper)
+ {
+ byte[] bs = new byte[16 * random.nextInt(upper)];
+ random.nextBytes(bs);
+ return bs;
+ }
+
+ private byte[] randomBytes(int upper)
+ {
+ byte[] bs = new byte[random.nextInt(upper)];
+ random.nextBytes(bs);
+ return bs;
+ }
+
+ private byte[] concatArrays(byte[] a, byte[] b) throws IOException
+ {
+ byte[] ab = new byte[a.length + b.length];
+ System.arraycopy(a, 0, ab, 0, a.length);
+ System.arraycopy(b, 0, ab, a.length, b.length);
+ return ab;
+ }
+
+ private byte[] combine_GHASH(byte[] ghashA_, long bitlenA, byte[] ghash_C, long bitlenC)
+ {
+ // Note: bitlenA must be aligned to the block size
+
+ long c = (bitlenC + 127) >>> 7;
+
+ byte[] H_c = new byte[16];
+ exp.exponentiateX(c, H_c);
+
+ byte[] tmp1 = lengthBlock(bitlenA, 0);
+ mul.multiplyH(tmp1);
+
+ byte[] ghashAC = Arrays.clone(ghashA_);
+ xor(ghashAC, tmp1);
+ ghashAC = multiply(ghashAC, H_c);
+ // No need to touch the len(C) part (second 8 bytes)
+ xor(ghashAC, tmp1);
+ xor(ghashAC, ghash_C);
+
+ return ghashAC;
+ }
+
+ private byte[] concatAuth_GHASH(byte[] ghashP, long bitlenP, byte[] ghashA, long bitlenA)
+ {
+ // Note: bitlenP must be aligned to the block size
+
+ long a = (bitlenA + 127) >>> 7;
+
+ byte[] tmp1 = lengthBlock(bitlenP, 0);
+ mul.multiplyH(tmp1);
+
+ byte[] tmp2 = lengthBlock(bitlenA ^ (bitlenP + bitlenA), 0);
+ mul.multiplyH(tmp2);
+
+ byte[] H_a = new byte[16];
+ exp.exponentiateX(a, H_a);
+
+ byte[] ghashC = Arrays.clone(ghashP);
+ xor(ghashC, tmp1);
+ ghashC = multiply(ghashC, H_a);
+ xor(ghashC, tmp2);
+ xor(ghashC, ghashA);
+ return ghashC;
+ }
+
+ private byte[] concatCrypt_GHASH(byte[] ghashP, long bitlenP, byte[] ghashA, long bitlenA)
+ {
+ // Note: bitlenP must be aligned to the block size
+
+ long a = (bitlenA + 127) >>> 7;
+
+ byte[] tmp1 = lengthBlock(0, bitlenP);
+ mul.multiplyH(tmp1);
+
+ byte[] tmp2 = lengthBlock(0, bitlenA ^ (bitlenP + bitlenA));
+ mul.multiplyH(tmp2);
+
+ byte[] H_a = new byte[16];
+ exp.exponentiateX(a, H_a);
+
+ byte[] ghashC = Arrays.clone(ghashP);
+ xor(ghashC, tmp1);
+ ghashC = multiply(ghashC, H_a);
+ xor(ghashC, tmp2);
+ xor(ghashC, ghashA);
+ return ghashC;
+ }
+
+ private byte[] GHASH(byte[] A, byte[] C)
+ {
+ byte[] X = new byte[16];
+
+ {
+ for (int pos = 0; pos < A.length; pos += 16)
+ {
+ byte[] tmp = new byte[16];
+ int num = Math.min(A.length - pos, 16);
+ System.arraycopy(A, pos, tmp, 0, num);
+ xor(X, tmp);
+ mul.multiplyH(X);
+ }
+ }
+
+ {
+ for (int pos = 0; pos < C.length; pos += 16)
+ {
+ byte[] tmp = new byte[16];
+ int num = Math.min(C.length - pos, 16);
+ System.arraycopy(C, pos, tmp, 0, num);
+ xor(X, tmp);
+ mul.multiplyH(X);
+ }
+ }
+
+ {
+ xor(X, lengthBlock((long)A.length * 8, (long)C.length * 8));
+ mul.multiplyH(X);
+ }
+
+ return X;
+ }
+
+ private static byte[] lengthBlock(long bitlenA, long bitlenC)
+ {
+ byte[] tmp = new byte[16];
+ Pack.longToBigEndian(bitlenA, tmp, 0);
+ Pack.longToBigEndian(bitlenC, tmp, 8);
+ return tmp;
+ }
+
+ private static void xor(byte[] block, byte[] val)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ block[i] ^= val[i];
+ }
+ }
+
+ private static byte[] multiply(byte[] a, byte[] b)
+ {
+ byte[] c = new byte[16];
+ byte[] tmp = Arrays.clone(b);
+
+ for (int i = 0; i < 16; ++i)
+ {
+ byte bits = a[i];
+ for (int j = 7; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(c, tmp);
+ }
+
+ boolean lsb = (tmp[15] & 1) != 0;
+ shiftRight(tmp);
+ if (lsb)
+ {
+ // R = new byte[]{ 0xe1, ... };
+// GCMUtil.xor(v, R);
+ tmp[0] ^= (byte)0xe1;
+ }
+ }
+ }
+
+ return c;
+ }
+
+ private static void shiftRight(byte[] block)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i] & 0xff;
+ block[i] = (byte) ((b >>> 1) | bit);
+ if (++i == 16)
+ {
+ break;
+ }
+ bit = (b & 1) << 7;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMTest.java
new file mode 100644
index 00000000..b34cc4e9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GCMTest.java
@@ -0,0 +1,687 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.modes.gcm.BasicGCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables64kGCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Times;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from "The Galois/Counter Mode of Operation (GCM)", McGrew/Viega, Appendix B
+ */
+public class GCMTest
+ extends SimpleTest
+{
+ private static final String[][] TEST_VECTORS = new String[][] {
+ {
+ "Test Case 1",
+ "00000000000000000000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "58e2fccefa7e3061367f1d57a4e7455a",
+ },
+ {
+ "Test Case 2",
+ "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "0388dace60b6a392f328c2b971b2fe78",
+ "ab6e47d42cec13bdf53a67b21257bddf",
+ },
+ {
+ "Test Case 3",
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "42831ec2217774244b7221b784d0d49c"
+ + "e3aa212f2c02a4e035c17e2329aca12e"
+ + "21d514b25466931c7d8f6a5aac84aa05"
+ + "1ba30b396a0aac973d58e091473f5985",
+ "4d5c2af327cd64a62cf35abd2ba6fab4",
+ },
+ {
+ "Test Case 4",
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbaddecaf888",
+ "42831ec2217774244b7221b784d0d49c"
+ + "e3aa212f2c02a4e035c17e2329aca12e"
+ + "21d514b25466931c7d8f6a5aac84aa05"
+ + "1ba30b396a0aac973d58e091",
+ "5bc94fbc3221a5db94fae95ae7121a47",
+ },
+ {
+ "Test Case 5",
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbad",
+ "61353b4c2806934a777ff51fa22a4755"
+ + "699b2a714fcdc6f83766e5f97b6c7423"
+ + "73806900e49f24b22b097544d4896b42"
+ + "4989b5e1ebac0f07c23f4598",
+ "3612d2e79e3b0785561be14aaca2fccb",
+ },
+ {
+ "Test Case 6",
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "9313225df88406e555909c5aff5269aa"
+ + "6a7a9538534f7da1e4c303d2a318a728"
+ + "c3c0c95156809539fcf0e2429a6b5254"
+ + "16aedbf5a0de6a57a637b39b",
+ "8ce24998625615b603a033aca13fb894"
+ + "be9112a5c3a211a8ba262a3cca7e2ca7"
+ + "01e4a9a4fba43c90ccdcb281d48c7c6f"
+ + "d62875d2aca417034c34aee5",
+ "619cc5aefffe0bfa462af43c1699d050",
+ },
+ {
+ "Test Case 7",
+ "00000000000000000000000000000000"
+ + "0000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "cd33b28ac773f74ba00ed1f312572435",
+ },
+ {
+ "Test Case 8",
+ "00000000000000000000000000000000"
+ + "0000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "98e7247c07f0fe411c267e4384b0f600",
+ "2ff58d80033927ab8ef4d4587514f0fb",
+ },
+ {
+ "Test Case 9",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "3980ca0b3c00e841eb06fac4872a2757"
+ + "859e1ceaa6efd984628593b40ca1e19c"
+ + "7d773d00c144c525ac619d18c84a3f47"
+ + "18e2448b2fe324d9ccda2710acade256",
+ "9924a7c8587336bfb118024db8674a14",
+ },
+ {
+ "Test Case 10",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbaddecaf888",
+ "3980ca0b3c00e841eb06fac4872a2757"
+ + "859e1ceaa6efd984628593b40ca1e19c"
+ + "7d773d00c144c525ac619d18c84a3f47"
+ + "18e2448b2fe324d9ccda2710",
+ "2519498e80f1478f37ba55bd6d27618c",
+ },
+ {
+ "Test Case 11",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbad",
+ "0f10f599ae14a154ed24b36e25324db8"
+ + "c566632ef2bbb34f8347280fc4507057"
+ + "fddc29df9a471f75c66541d4d4dad1c9"
+ + "e93a19a58e8b473fa0f062f7",
+ "65dcc57fcf623a24094fcca40d3533f8",
+ },
+ {
+ "Test Case 12",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "9313225df88406e555909c5aff5269aa"
+ + "6a7a9538534f7da1e4c303d2a318a728"
+ + "c3c0c95156809539fcf0e2429a6b5254"
+ + "16aedbf5a0de6a57a637b39b",
+ "d27e88681ce3243c4830165a8fdcf9ff"
+ + "1de9a1d8e6b447ef6ef7b79828666e45"
+ + "81e79012af34ddd9e2f037589b292db3"
+ + "e67c036745fa22e7e9b7373b",
+ "dcf566ff291c25bbb8568fc3d376a6d9",
+ },
+ {
+ "Test Case 13",
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "530f8afbc74536b9a963b4f1c4cb738b",
+ },
+ {
+ "Test Case 14",
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "cea7403d4d606b6e074ec5d3baf39d18",
+ "d0d1c8a799996bf0265b98b5d48ab919",
+ },
+ {
+ "Test Case 15",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "522dc1f099567d07f47f37a32a84427d"
+ + "643a8cdcbfe5c0c97598a2bd2555d1aa"
+ + "8cb08e48590dbb3da7b08b1056828838"
+ + "c5f61e6393ba7a0abcc9f662898015ad",
+ "b094dac5d93471bdec1a502270e3cc6c",
+ },
+ {
+ "Test Case 16",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbaddecaf888",
+ "522dc1f099567d07f47f37a32a84427d"
+ + "643a8cdcbfe5c0c97598a2bd2555d1aa"
+ + "8cb08e48590dbb3da7b08b1056828838"
+ + "c5f61e6393ba7a0abcc9f662",
+ "76fc6ece0f4e1768cddf8853bb2d551b",
+ },
+ {
+ "Test Case 17",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "cafebabefacedbad",
+ "c3762df1ca787d32ae47c13bf19844cb"
+ + "af1ae14d0b976afac52ff7d79bba9de0"
+ + "feb582d33934a4f0954cc2363bc73f78"
+ + "62ac430e64abe499f47c9b1f",
+ "3a337dbf46a792c45e454913fe2ea8f2",
+ },
+ {
+ "Test Case 18",
+ "feffe9928665731c6d6a8f9467308308"
+ + "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a"
+ + "86a7a9531534f7da2e4c303d8a318a72"
+ + "1c3c0c95956809532fcf0e2449a6b525"
+ + "b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeef"
+ + "abaddad2",
+ "9313225df88406e555909c5aff5269aa"
+ + "6a7a9538534f7da1e4c303d2a318a728"
+ + "c3c0c95156809539fcf0e2429a6b5254"
+ + "16aedbf5a0de6a57a637b39b",
+ "5a8def2f0c9e53f1f75d7853659e2a20"
+ + "eeb2b22aafde6419a058ab4f6f746bf4"
+ + "0fc0c3b780f244452da3ebf1c5d82cde"
+ + "a2418997200ef82e44ae7e3f",
+ "a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
+ },
+ };
+
+ public String getName()
+ {
+ return "GCM";
+ }
+
+ public void performTest() throws Exception
+ {
+ for (int i = 0; i < TEST_VECTORS.length; ++i)
+ {
+ runTestCase(TEST_VECTORS[i]);
+ }
+
+ randomTests();
+ outputSizeTests();
+ testExceptions();
+ }
+
+ protected BlockCipher createAESEngine()
+ {
+ return new AESFastEngine();
+ }
+
+ private void testExceptions() throws InvalidCipherTextException
+ {
+ GCMBlockCipher gcm = new GCMBlockCipher(createAESEngine());
+
+ try
+ {
+ gcm = new GCMBlockCipher(new DESEngine());
+
+ fail("incorrect block size not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ gcm.init(false, new KeyParameter(new byte[16]));
+
+ fail("illegal argument not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ AEADTestUtil.testReset(this, new GCMBlockCipher(createAESEngine()), new GCMBlockCipher(createAESEngine()), new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[16]));
+ AEADTestUtil.testTampering(this, gcm, new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[16]));
+ AEADTestUtil.testOutputSizes(this, new GCMBlockCipher(createAESEngine()), new AEADParameters(new KeyParameter(
+ new byte[16]), 128, new byte[16]));
+ AEADTestUtil.testBufferSizeChecks(this, new GCMBlockCipher(createAESEngine()), new AEADParameters(
+ new KeyParameter(new byte[16]), 128, new byte[16]));
+ }
+
+ private void runTestCase(String[] testVector)
+ throws InvalidCipherTextException
+ {
+ for (int macLength = 12; macLength <= 16; ++macLength)
+ {
+ runTestCase(testVector, macLength);
+ }
+ }
+
+ private void runTestCase(String[] testVector, int macLength)
+ throws InvalidCipherTextException
+ {
+ int pos = 0;
+ String testName = testVector[pos++];
+ byte[] K = Hex.decode(testVector[pos++]);
+ byte[] P = Hex.decode(testVector[pos++]);
+ byte[] A = Hex.decode(testVector[pos++]);
+ byte[] IV = Hex.decode(testVector[pos++]);
+ byte[] C = Hex.decode(testVector[pos++]);
+
+ // For short MAC, take leading bytes
+ byte[] t = Hex.decode(testVector[pos++]);
+ byte[] T = new byte[macLength];
+ System.arraycopy(t, 0, T, 0, T.length);
+
+ // Default multiplier
+ runTestCase(null, null, testName, K, IV, A, P, C, T);
+
+ runTestCase(new BasicGCMMultiplier(), new BasicGCMMultiplier(), testName, K, IV, A, P, C, T);
+ runTestCase(new Tables8kGCMMultiplier(), new Tables8kGCMMultiplier(), testName, K, IV, A, P, C, T);
+ runTestCase(new Tables64kGCMMultiplier(), new Tables64kGCMMultiplier(), testName, K, IV, A, P, C, T);
+ }
+
+ private void runTestCase(
+ GCMMultiplier encM,
+ GCMMultiplier decM,
+ String testName,
+ byte[] K,
+ byte[] IV,
+ byte[] A,
+ byte[] P,
+ byte[] C,
+ byte[] T)
+ throws InvalidCipherTextException
+ {
+ byte[] fa = new byte[A.length / 2];
+ byte[] la = new byte[A.length - (A.length / 2)];
+ System.arraycopy(A, 0, fa, 0, fa.length);
+ System.arraycopy(A, fa.length, la, 0, la.length);
+
+ runTestCase(encM, decM, testName + " all initial associated data", K, IV, A, null, P, C, T);
+ runTestCase(encM, decM, testName + " all subsequent associated data", K, IV, null, A, P, C, T);
+ runTestCase(encM, decM, testName + " split associated data", K, IV, fa, la, P, C, T);
+ }
+
+ private void runTestCase(
+ GCMMultiplier encM,
+ GCMMultiplier decM,
+ String testName,
+ byte[] K,
+ byte[] IV,
+ byte[] A,
+ byte[] SA,
+ byte[] P,
+ byte[] C,
+ byte[] T)
+ throws InvalidCipherTextException
+ {
+ AEADParameters parameters = new AEADParameters(new KeyParameter(K), T.length * 8, IV, A);
+ GCMBlockCipher encCipher = initCipher(encM, true, parameters);
+ GCMBlockCipher decCipher = initCipher(decM, false, parameters);
+ checkTestCase(encCipher, decCipher, testName, SA, P, C, T);
+ checkTestCase(encCipher, decCipher, testName + " (reused)", SA, P, C, T);
+
+ // Key reuse
+ AEADParameters keyReuseParams = AEADTestUtil.reuseKey(parameters);
+ encCipher.init(true, keyReuseParams);
+ decCipher.init(false, keyReuseParams);
+ checkTestCase(encCipher, decCipher, testName + " (key reuse)", SA, P, C, T);
+ }
+
+ private GCMBlockCipher initCipher(GCMMultiplier m, boolean forEncryption, AEADParameters parameters)
+ {
+ GCMBlockCipher c = new GCMBlockCipher(createAESEngine(), m);
+ c.init(forEncryption, parameters);
+ return c;
+ }
+
+ private void checkTestCase(
+ GCMBlockCipher encCipher,
+ GCMBlockCipher decCipher,
+ String testName,
+ byte[] SA,
+ byte[] P,
+ byte[] C,
+ byte[] T)
+ throws InvalidCipherTextException
+ {
+ byte[] enc = new byte[encCipher.getOutputSize(P.length)];
+ if (SA != null)
+ {
+ encCipher.processAADBytes(SA, 0, SA.length);
+ }
+ int len = encCipher.processBytes(P, 0, P.length, enc, 0);
+ len += encCipher.doFinal(enc, len);
+
+ if (enc.length != len)
+ {
+// System.out.println("" + enc.length + "/" + len);
+ fail("encryption reported incorrect length: " + testName);
+ }
+
+ byte[] mac = encCipher.getMac();
+
+ byte[] data = new byte[P.length];
+ System.arraycopy(enc, 0, data, 0, data.length);
+ byte[] tail = new byte[enc.length - P.length];
+ System.arraycopy(enc, P.length, tail, 0, tail.length);
+
+ if (!areEqual(C, data))
+ {
+ fail("incorrect encrypt in: " + testName);
+ }
+
+ if (!areEqual(T, mac))
+ {
+ fail("getMac() returned wrong mac in: " + testName);
+ }
+
+ if (!areEqual(T, tail))
+ {
+ fail("stream contained wrong mac in: " + testName);
+ }
+
+ byte[] dec = new byte[decCipher.getOutputSize(enc.length)];
+ if (SA != null)
+ {
+ decCipher.processAADBytes(SA, 0, SA.length);
+ }
+ len = decCipher.processBytes(enc, 0, enc.length, dec, 0);
+ len += decCipher.doFinal(dec, len);
+ mac = decCipher.getMac();
+
+ data = new byte[C.length];
+ System.arraycopy(dec, 0, data, 0, data.length);
+
+ if (!areEqual(P, data))
+ {
+ fail("incorrect decrypt in: " + testName);
+ }
+ }
+
+ private void randomTests()
+ throws InvalidCipherTextException
+ {
+ SecureRandom srng = new SecureRandom();
+ srng.setSeed(Times.nanoTime());
+ randomTests(srng, null);
+ randomTests(srng, new BasicGCMMultiplier());
+ randomTests(srng, new Tables8kGCMMultiplier());
+ randomTests(srng, new Tables64kGCMMultiplier());
+ }
+
+ private void randomTests(SecureRandom srng, GCMMultiplier m)
+ throws InvalidCipherTextException
+ {
+ for (int i = 0; i < 10; ++i)
+ {
+ randomTest(srng, m);
+ }
+ }
+
+ private void randomTest(SecureRandom srng, GCMMultiplier m)
+ throws InvalidCipherTextException
+ {
+ int kLength = 16 + 8 * (Math.abs(srng.nextInt()) % 3);
+ byte[] K = new byte[kLength];
+ srng.nextBytes(K);
+
+ int pLength = srng.nextInt() >>> 16;
+ byte[] P = new byte[pLength];
+ srng.nextBytes(P);
+
+ int aLength = srng.nextInt() >>> 24;
+ byte[] A = new byte[aLength];
+ srng.nextBytes(A);
+
+ int saLength = srng.nextInt() >>> 24;
+ byte[] SA = new byte[saLength];
+ srng.nextBytes(SA);
+
+ int ivLength = 1 + (srng.nextInt() >>> 24);
+ byte[] IV = new byte[ivLength];
+ srng.nextBytes(IV);
+
+ AEADParameters parameters = new AEADParameters(new KeyParameter(K), 16 * 8, IV, A);
+ GCMBlockCipher cipher = initCipher(m, true, parameters);
+ byte[] C = new byte[cipher.getOutputSize(P.length)];
+ int predicted = cipher.getUpdateOutputSize(P.length);
+
+ int split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ int len = cipher.processBytes(P, 0, P.length, C, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ if (predicted != len)
+ {
+ fail("encryption reported incorrect update length in randomised test");
+ }
+
+ len += cipher.doFinal(C, len);
+
+ if (C.length != len)
+ {
+ fail("encryption reported incorrect length in randomised test");
+ }
+
+ byte[] encT = cipher.getMac();
+ byte[] tail = new byte[C.length - P.length];
+ System.arraycopy(C, P.length, tail, 0, tail.length);
+
+ if (!areEqual(encT, tail))
+ {
+ fail("stream contained wrong mac in randomised test");
+ }
+
+ cipher.init(false, parameters);
+ byte[] decP = new byte[cipher.getOutputSize(C.length)];
+ predicted = cipher.getUpdateOutputSize(C.length);
+
+ split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ len = cipher.processBytes(C, 0, C.length, decP, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ if (predicted != len)
+ {
+ fail("decryption reported incorrect update length in randomised test");
+ }
+
+ len += cipher.doFinal(decP, len);
+
+ if (!areEqual(P, decP))
+ {
+ fail("incorrect decrypt in randomised test");
+ }
+
+ byte[] decT = cipher.getMac();
+ if (!areEqual(encT, decT))
+ {
+ fail("decryption produced different mac from encryption");
+ }
+
+ //
+ // key reuse test
+ //
+ cipher.init(false, AEADTestUtil.reuseKey(parameters));
+ decP = new byte[cipher.getOutputSize(C.length)];
+
+ split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ len = cipher.processBytes(C, 0, C.length, decP, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ len += cipher.doFinal(decP, len);
+
+ if (!areEqual(P, decP))
+ {
+ fail("incorrect decrypt in randomised test");
+ }
+
+ decT = cipher.getMac();
+ if (!areEqual(encT, decT))
+ {
+ fail("decryption produced different mac from encryption");
+ }
+ }
+
+ private void outputSizeTests()
+ {
+ byte[] K = new byte[16];
+ byte[] A = null;
+ byte[] IV = new byte[16];
+
+ AEADParameters parameters = new AEADParameters(new KeyParameter(K), 16 * 8, IV, A);
+ GCMBlockCipher cipher = initCipher(null, true, parameters);
+
+ if (cipher.getUpdateOutputSize(0) != 0)
+ {
+ fail("incorrect getUpdateOutputSize for initial 0 bytes encryption");
+ }
+
+ if (cipher.getOutputSize(0) != 16)
+ {
+ fail("incorrect getOutputSize for initial 0 bytes encryption");
+ }
+
+ cipher.init(false, parameters);
+
+ if (cipher.getUpdateOutputSize(0) != 0)
+ {
+ fail("incorrect getUpdateOutputSize for initial 0 bytes decryption");
+ }
+
+ // NOTE: 0 bytes would be truncated data, but we want it to fail in the doFinal, not here
+ if (cipher.getOutputSize(0) != 0)
+ {
+ fail("fragile getOutputSize for initial 0 bytes decryption");
+ }
+
+ if (cipher.getOutputSize(16) != 0)
+ {
+ fail("incorrect getOutputSize for initial MAC-size bytes decryption");
+ }
+ }
+
+ private static int nextInt(SecureRandom rand, int n)
+ {
+ if ((n & -n) == n) // i.e., n is a power of 2
+ {
+ return (int)((n * (long)(rand.nextInt() >>> 1)) >> 31);
+ }
+
+ int bits, value;
+ do
+ {
+ bits = rand.nextInt() >>> 1;
+ value = bits % n;
+ }
+ while (bits - value + (n - 1) < 0);
+
+ return value;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new GCMTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GMacTest.java
new file mode 100644
index 00000000..082aac61
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GMacTest.java
@@ -0,0 +1,174 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.macs.GMac;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors for AES-GMAC, extracted from <a
+ * href="http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip">NIST CAVP GCM test
+ * vectors</a>.
+ *
+ */
+public class GMacTest extends SimpleTest
+{
+ private static class TestCase
+ {
+ private byte[] key;
+ private byte[] iv;
+ private byte[] ad;
+ private byte[] tag;
+ private String name;
+
+ private TestCase(final String name, final String key, final String iv, final String ad, final String tag)
+ {
+ this.name = name;
+ this.key = Hex.decode(key);
+ this.iv = Hex.decode(iv);
+ this.ad = Hex.decode(ad);
+ this.tag = Hex.decode(tag);
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public byte[] getIv()
+ {
+ return iv;
+ }
+
+ public byte[] getAd()
+ {
+ return ad;
+ }
+
+ public byte[] getTag()
+ {
+ return tag;
+ }
+ }
+
+ private static TestCase[] TEST_VECTORS = new TestCase[] {
+ // Count = 0, from each of the PTlen = 0 test vector sequences
+ new TestCase("128/96/0/128", "11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "",
+ "250327c674aaf477aef2675748cf6971"),
+ new TestCase("128/96/0/120", "272f16edb81a7abbea887357a58c1917", "794ec588176c703d3d2a7a07", "",
+ "b6e6f197168f5049aeda32dafbdaeb"),
+ new TestCase("128/96/0/112", "81b6844aab6a568c4556a2eb7eae752f", "ce600f59618315a6829bef4d", "",
+ "89b43e9dbc1b4f597dbbc7655bb5"),
+ new TestCase("128/96/0/104", "cde2f9a9b1a004165ef9dc981f18651b", "29512c29566c7322e1e33e8e", "",
+ "2e58ce7dabd107c82759c66a75"),
+ new TestCase("128/96/0/96", "b01e45cc3088aaba9fa43d81d481823f", "5a2c4a66468713456a4bd5e1", "",
+ "014280f944f53c681164b2ff"),
+
+ new TestCase("128/96/128/128", "77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3",
+ "7a43ec1d9c0a5a78a0b16533a6213cab", "209fcc8d3675ed938e9c7166709dd946"),
+ new TestCase("128/96/128/96", "bea48ae4980d27f357611014d4486625", "32bddb5c3aa998a08556454c",
+ "8a50b0b8c7654bced884f7f3afda2ead", "8e0f6d8bf05ffebe6f500eb1"),
+
+ new TestCase("128/96/384/128", "99e3e8793e686e571d8285c564f75e2b", "c2dd0ab868da6aa8ad9c0d23",
+ "b668e42d4e444ca8b23cfdd95a9fedd5178aa521144890b093733cf5cf22526c5917ee476541809ac6867a8c399309fc",
+ "3f4fba100eaf1f34b0baadaae9995d85"),
+ new TestCase("128/96/384/96", "c77acd1b0918e87053cb3e51651e7013", "39ff857a81745d10f718ac00",
+ "407992f82ea23b56875d9a3cb843ceb83fd27cb954f7c5534d58539fe96fb534502a1b38ea4fac134db0a42de4be1137",
+ "2a5dc173285375dc82835876"),
+
+ new TestCase(
+ "128/1024/0/128",
+ "d0f1f4defa1e8c08b4b26d576392027c",
+ "42b4f01eb9f5a1ea5b1eb73b0fb0baed54f387ecaa0393c7d7dffc6af50146ecc021abf7eb9038d4303d91f8d741a11743166c0860208bcc02c6258fd9511a2fa626f96d60b72fcff773af4e88e7a923506e4916ecbd814651e9f445adef4ad6a6b6c7290cc13b956130eef5b837c939fcac0cbbcc9656cd75b13823ee5acdac",
+ "", "7ab49b57ddf5f62c427950111c5c4f0d"),
+ new TestCase(
+ "128/1024/384/96",
+ "3cce72d37933394a8cac8a82deada8f0",
+ "aa2f0d676d705d9733c434e481972d4888129cf7ea55c66511b9c0d25a92a174b1e28aa072f27d4de82302828955aadcb817c4907361869bd657b45ff4a6f323871987fcf9413b0702d46667380cd493ed24331a28b9ce5bbfa82d3a6e7679fcce81254ba64abcad14fd18b22c560a9d2c1cd1d3c42dac44c683edf92aced894",
+ "5686b458e9c176f4de8428d9ebd8e12f569d1c7595cf49a4b0654ab194409f86c0dd3fdb8eb18033bb4338c70f0b97d1",
+ "a3a9444b21f330c3df64c8b6"), };
+
+ public void performTest()
+ {
+ for (int i = 0; i < TEST_VECTORS.length; i++)
+ {
+ TestCase testCase = TEST_VECTORS[i];
+
+ Mac mac = new GMac(new GCMBlockCipher(new AESFastEngine()), testCase.getTag().length * 8);
+ CipherParameters key = new KeyParameter(testCase.getKey());
+ mac.init(new ParametersWithIV(key, testCase.getIv()));
+
+ testSingleByte(mac, testCase);
+ testMultibyte(mac, testCase);
+ }
+
+ // Invalid mac size
+ testInvalidMacSize(97);
+ testInvalidMacSize(136);
+ testInvalidMacSize(24);
+ }
+
+ private void testInvalidMacSize(int size)
+ {
+ try
+ {
+ GMac mac = new GMac(new GCMBlockCipher(new AESFastEngine()), size);
+ mac.init(new ParametersWithIV(null, new byte[16]));
+ fail("Expected failure for illegal mac size " + size);
+ }
+ catch (IllegalArgumentException e)
+ {
+ if (!e.getMessage().startsWith("Invalid value for MAC size"))
+ {
+ fail("Illegal mac size failed with unexpected message");
+ }
+ }
+ }
+
+ private void testMultibyte(Mac mac, TestCase testCase)
+ {
+ mac.update(testCase.getAd(), 0, testCase.getAd().length);
+ checkMac(mac, testCase);
+ }
+
+ private void testSingleByte(Mac mac, TestCase testCase)
+ {
+ final byte[] ad = testCase.getAd();
+ for (int i = 0; i < ad.length; i++)
+ {
+ mac.update(ad[i]);
+ }
+ checkMac(mac, testCase);
+ }
+
+ private void checkMac(Mac mac, TestCase testCase)
+ {
+ final byte[] generatedMac = new byte[mac.getMacSize()];
+ mac.doFinal(generatedMac, 0);
+ if (!areEqual(testCase.getTag(), generatedMac))
+ {
+ fail("Failed " + testCase.getName() + " - expected " + new String(Hex.encode(testCase.getTag())) + " got "
+ + new String(Hex.encode(generatedMac)));
+ }
+ }
+
+ public String getName()
+ {
+ return "GMac";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new GMacTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST28147MacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST28147MacTest.java
new file mode 100644
index 00000000..8faaca1a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST28147MacTest.java
@@ -0,0 +1,89 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.GOST28147Engine;
+import org.bouncycastle.crypto.macs.GOST28147Mac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * GOST 28147 MAC tester
+ */
+public class GOST28147MacTest
+ implements Test
+{
+ //
+ // these GOSTMac for testing.
+ //
+ static byte[] gkeyBytes1 = Hex.decode("6d145dc993f4019e104280df6fcd8cd8e01e101e4c113d7ec4f469ce6dcd9e49");
+ static byte[] gkeyBytes2 = Hex.decode("6d145dc993f4019e104280df6fcd8cd8e01e101e4c113d7ec4f469ce6dcd9e49");
+
+ static byte[] input3 = Hex.decode("7768617420646f2079612077616e7420666f72206e6f7468696e673f");
+ static byte[] input4 = Hex.decode("7768617420646f2079612077616e7420666f72206e6f7468696e673f");
+
+ static byte[] output7 = Hex.decode("93468a46");
+ static byte[] output8 = Hex.decode("93468a46");
+
+ public GOST28147MacTest()
+ {
+ }
+
+ public TestResult perform()
+ {
+ // test1
+ Mac mac = new GOST28147Mac();
+ KeyParameter key = new KeyParameter(gkeyBytes1);
+
+ mac.init(key);
+
+ mac.update(input3, 0, input3.length);
+
+ byte[] out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!Arrays.areEqual(out, output7))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed test 1 - expected " + new String(Hex.encode(output7)) + " got " + new String(Hex.encode(out)));
+ }
+
+ // test2
+ key = new KeyParameter(gkeyBytes2);
+
+ ParametersWithSBox gparam = new ParametersWithSBox(key, GOST28147Engine.getSBox("E-A"));
+
+ mac.init(gparam);
+
+ mac.update(input4, 0, input4.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!Arrays.areEqual(out, output8))
+ {
+ return new SimpleTestResult(false, getName() + ": Failed test 2 - expected " + new String(Hex.encode(output8)) + " got " + new String(Hex.encode(out)));
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public String getName()
+ {
+ return "GOST28147Mac";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ GOST28147MacTest test = new GOST28147MacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST28147Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST28147Test.java
new file mode 100644
index 00000000..8de84406
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST28147Test.java
@@ -0,0 +1,328 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.digests.GOST3411Digest;
+import org.bouncycastle.crypto.engines.GOST28147Engine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.GOFBBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class GOST28147Test
+ extends CipherTest
+{
+ static String input1 = "0000000000000000";
+ static String output1 = "1b0bbc32cebcab42";
+ static String input2 = "bc350e71aac5f5c2";
+ static String output2 = "d35ab653493b49f5";
+ static String input3 = "bc350e71aa11345709acde";
+ static String output3 = "8824c124c4fd14301fb1e8";
+ static String input4 = "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f";
+ static String output4 = "29b7083e0a6d955ca0ec5b04fdb4ea41949f1dd2efdf17baffc1780b031f3934";
+
+ static byte TestSBox[] = {
+ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
+ 0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,
+ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
+ 0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,
+ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
+ 0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,
+ 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,
+ 0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0
+ };
+
+ static SimpleTest[] tests =
+ { new BlockCipherVectorTest(1, new GOST28147Engine(),
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")),
+ input1, output1),
+ new BlockCipherVectorTest(2, new CBCBlockCipher(new GOST28147Engine()),
+ new ParametersWithIV(new KeyParameter(Hex.decode("00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF")),
+ Hex.decode("1234567890abcdef")), input2, output2),
+ new BlockCipherVectorTest(3, new GOFBBlockCipher(new GOST28147Engine()),
+ new ParametersWithIV(new KeyParameter(Hex.decode("0011223344556677889900112233445566778899001122334455667788990011")),
+ Hex.decode("1234567890abcdef")), //IV
+ input3, output3),
+ new BlockCipherVectorTest(4, new CFBBlockCipher(new GOST28147Engine(), 64),
+ new ParametersWithIV(new KeyParameter(Hex.decode("aafd12f659cae63489b479e5076ddec2f06cb58faafd12f659cae63489b479e5")),
+ Hex.decode("aafd12f659cae634")), input4, output4),
+
+ //tests with parameters, set S-box.
+ new BlockCipherVectorTest(5, new GOST28147Engine(),
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")),//key , default parameter S-box set to D-Test
+ input1, output1),
+ new BlockCipherVectorTest(6, new CFBBlockCipher(new GOST28147Engine(), 64),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("D-Test")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "b587f7a0814c911d"), //encrypt message
+ new BlockCipherVectorTest(7, new CFBBlockCipher(new GOST28147Engine(), 64),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-Test")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "e8287f53f991d52b"), //encrypt message
+ new BlockCipherVectorTest(8, new CFBBlockCipher(new GOST28147Engine(), 64),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "c41009dba22ebe35"), //encrypt message
+ new BlockCipherVectorTest(9, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-B")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "80d8723fcd3aba28"), //encrypt message
+ new BlockCipherVectorTest(10, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-C")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "739f6f95068499b5"), //encrypt message
+ new BlockCipherVectorTest(11, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("E-D")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "4663f720f4340f57"), //encrypt message
+ new BlockCipherVectorTest(12, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ GOST28147Engine.getSBox("D-A")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "5bb0a31d218ed564"), //encrypt message
+ new BlockCipherVectorTest(13, new CFBBlockCipher(new GOST28147Engine(), 8),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("546d203368656c326973652073736e62206167796967747473656865202c3d73")), //key
+ TestSBox), //set own S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "0000000000000000", //input message
+ "c3af96ef788667c5"), //encrypt message
+ new BlockCipherVectorTest(14, new GOFBBlockCipher(new GOST28147Engine()),
+ new ParametersWithIV(
+ new ParametersWithSBox(
+ new KeyParameter(Hex.decode("4ef72b778f0b0bebeef4f077551cb74a927b470ad7d7f2513454569a247e989d")), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("1234567890abcdef")), //IV
+ "bc350e71aa11345709acde", //input message
+ "1bcc2282707c676fb656dc"), //encrypt message
+
+ };
+
+ static private final int GOST28147_KEY_LENGTH = 32;
+
+ private byte[] generateKey(byte[] startkey)
+ {
+ byte[] newKey = new byte[GOST28147_KEY_LENGTH];
+
+ GOST3411Digest digest = new GOST3411Digest();
+
+ digest.update(startkey, 0, startkey.length);
+ digest.doFinal(newKey, 0);
+
+ return newKey;
+ }
+
+ GOST28147Test()
+ {
+ super(tests, new GOST28147Engine(), new KeyParameter(new byte[32]));
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ //advanced tests with GOST28147KeyGenerator:
+ //encrypt on hesh message; ECB mode:
+ byte[] in = Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20");
+ byte[] output = Hex.decode("8ad3c8f56b27ff1fbd46409359bdc796bc350e71aac5f5c0");
+ byte[] out = new byte[in.length];
+
+ byte[] key = generateKey(Hex.decode("0123456789abcdef")); //!!! heshing start_key - get 256 bits !!!
+// System.out.println(new String(Hex.encode(key)));
+ CipherParameters param = new ParametersWithSBox(new KeyParameter(key), GOST28147Engine.getSBox("E-A"));
+ //CipherParameters param = new GOST28147Parameters(key,"D-Test");
+ BufferedBlockCipher cipher = new BufferedBlockCipher(new GOST28147Engine());
+
+ cipher.init(true, param);
+ int len1 = cipher.processBytes(in, 0, in.length, out, 0);
+ try
+ {
+ cipher.doFinal(out, len1);
+ }
+ catch (CryptoException e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+ if (out.length != output.length)
+ {
+ fail("failed - "
+ + "expected " + new String(Hex.encode(output)) + " got "
+ + new String(Hex.encode(out)));
+ }
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+
+ //encrypt on hesh message; CFB mode:
+ in = Hex.decode("bc350e71aac5f5c2");
+ output = Hex.decode("0ebbbafcf38f14a5");
+ out = new byte[in.length];
+
+ key = generateKey(Hex.decode("0123456789abcdef")); //!!! heshing start_key - get 256 bits !!!
+ param = new ParametersWithIV(new ParametersWithSBox(
+ new KeyParameter(key), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("1234567890abcdef")); //IV
+
+ cipher = new BufferedBlockCipher(new CFBBlockCipher(new GOST28147Engine(), 64));
+
+ cipher.init(true, param);
+ len1 = cipher.processBytes(in, 0, in.length, out, 0);
+ try
+ {
+ cipher.doFinal(out, len1);
+ }
+ catch (CryptoException e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+ if (out.length != output.length)
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+
+ //encrypt on hesh message; CFB mode:
+ in = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f");
+ output = Hex.decode("64988982819f0a1655e226e19ecad79d10cc73bac95c5d7da034786c12294225");
+ out = new byte[in.length];
+
+ key = generateKey(Hex.decode("aafd12f659cae63489b479e5076ddec2f06cb58faafd12f659cae63489b479e5")); //!!! heshing start_key - get 256 bits !!!
+ param = new ParametersWithIV(new ParametersWithSBox(
+ new KeyParameter(key), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("aafd12f659cae634")); //IV
+
+ cipher = new BufferedBlockCipher(new CFBBlockCipher(new GOST28147Engine(), 64));
+
+ cipher.init(true, param);
+ len1 = cipher.processBytes(in, 0, in.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (out.length != output.length)
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ //encrypt on hesh message; OFB mode:
+ in = Hex.decode("bc350e71aa11345709acde");
+ output = Hex.decode("1bcc2282707c676fb656dc");
+ out = new byte[in.length];
+
+ key = generateKey(Hex.decode("0123456789abcdef")); //!!! heshing start_key - get 256 bits !!!
+ param = new ParametersWithIV(new ParametersWithSBox(
+ new KeyParameter(key), //key
+ GOST28147Engine.getSBox("E-A")), //type S-box
+ Hex.decode("1234567890abcdef")); //IV
+
+ cipher = new BufferedBlockCipher(new GOFBBlockCipher(new GOST28147Engine()));
+
+ cipher.init(true, param);
+ len1 = cipher.processBytes(in, 0, in.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (out.length != output.length)
+ {
+ fail("failed - " + "expected "
+ + new String(Hex.encode(output)) + " got "
+ + new String(Hex.encode(out)));
+ }
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ // key reuse test
+ param = new ParametersWithIV(null, // key and sbox reused
+ Hex.decode("1234567890abcdef")); //IV
+
+ cipher.init(true, param);
+ len1 = cipher.processBytes(in, 0, in.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (out.length != output.length)
+ {
+ fail("failed - " + "expected "
+ + new String(Hex.encode(output)) + " got "
+ + new String(Hex.encode(out)));
+ }
+ for (int i = 0; i != out.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+ }
+
+ public String getName()
+ {
+ return "GOST28147";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new GOST28147Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3410Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3410Test.java
new file mode 100644
index 00000000..17de3fee
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3410Test.java
@@ -0,0 +1,1570 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.GOST3410KeyPairGenerator;
+import org.bouncycastle.crypto.generators.GOST3410ParametersGenerator;
+import org.bouncycastle.crypto.params.GOST3410KeyGenerationParameters;
+import org.bouncycastle.crypto.params.GOST3410Parameters;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.GOST3410Signer;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.FixedSecureRandom;
+import org.bouncycastle.util.test.NumberParsing;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class GOST3410Test
+ implements Test
+{
+ byte[] hashmessage = Hex.decode("3042453136414534424341374533364339313734453431443642453241453435");
+
+ private byte[] zeroTwo(int length)
+ {
+ byte[] data = new byte[length];
+ data[data.length - 1] = 0x02;
+ return data;
+ }
+
+ private class GOST3410_TEST1_512
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-TEST1-512";
+ }
+
+ FixedSecureRandom init_random = new FixedSecureRandom(new byte[][] { Hex.decode("00005EC900007341"), zeroTwo(64) });
+ FixedSecureRandom random = new FixedSecureRandom(Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A"));
+ FixedSecureRandom keyRandom = new FixedSecureRandom(Hex.decode("3036314538303830343630454235324435324234314132373832433138443046"));
+
+ BigInteger pValue = new BigInteger("EE8172AE8996608FB69359B89EB82A69854510E2977A4D63BC97322CE5DC3386EA0A12B343E9190F23177539845839786BB0C345D165976EF2195EC9B1C379E3", 16);
+ BigInteger qValue = new BigInteger("98915E7EC8265EDFCDA31E88F24809DDB064BDC7285DD50D7289F0AC6F49DD2D", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("3e5f895e276d81d2d52c0763270a458157b784c57abdbd807bc44fd43a32ac06",16);
+ BigInteger s = new BigInteger("3f0dd5d4400d47c08e4ce505ff7434b6dbf729592e37c74856dab85115a60955",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(512, 1, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (params.getValidationParameters() == null)
+ {
+ return new SimpleTestResult(false, getName() + "validation parameters wrong");
+ }
+ if (params.getValidationParameters().getC() != 29505
+ || params.getValidationParameters().getX0() != 24265)
+ {
+ return new SimpleTestResult(false, getName() + "validation parameters values wrong");
+ }
+ if (!init_random.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'init_random'.");
+ }
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ if (!keyRandom.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'keyRandom'.");
+ }
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer gost3410 = new GOST3410Signer();
+
+ gost3410.init(true, param);
+
+ BigInteger[] sig = gost3410.generateSignature(hashmessage);
+
+ if (!random.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'random'.");
+ }
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ gost3410.init(false, pair.getPublic());
+
+ if (gost3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_TEST2_512
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-TEST2-512";
+ }
+
+ FixedSecureRandom init_random = new FixedSecureRandom(new byte[][] { Hex.decode("000000003DFC46F1000000000000000D"), zeroTwo(64) });
+ FixedSecureRandom random = new FixedSecureRandom(Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A"));
+ FixedSecureRandom keyRandom = new FixedSecureRandom(Hex.decode("3036314538303830343630454235324435324234314132373832433138443046"));
+
+ BigInteger pValue = new BigInteger("8b08eb135af966aab39df294538580c7da26765d6d38d30cf1c06aae0d1228c3316a0e29198460fad2b19dc381c15c888c6dfd0fc2c565abb0bf1faff9518f85", 16);
+ BigInteger qValue = new BigInteger("931a58fb6f0dcdf2fe7549bc3f19f4724b56898f7f921a076601edb18c93dc75", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("7c07c8cf035c2a1cb2b7fae5807ac7cd623dfca7a1a68f6d858317822f1ea00d",16);
+ BigInteger s = new BigInteger("7e9e036a6ff87dbf9b004818252b1f6fc310bdd4d17cb8c37d9c36c7884de60c",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(512, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!init_random.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'init_random'.");
+ }
+
+ if (params.getValidationParameters() == null)
+ {
+ return new SimpleTestResult(false, getName() + ": validation parameters wrong");
+ }
+
+ if (params.getValidationParameters().getCL() != 13
+ || params.getValidationParameters().getX0L() != 1039943409)
+ {
+ return new SimpleTestResult(false, getName() + ": validation parameters values wrong");
+ }
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ if (!keyRandom.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'keyRandom'.");
+ }
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!random.isExhausted())
+ {
+ return new SimpleTestResult(false, getName()
+ + ": unexpected number of bytes used from 'random'.");
+ }
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_TEST1_1024
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-TEST1-1024";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstInt = true;
+
+ public int nextInt()
+ {
+ String x0 = "0xA565";
+ String c = "0x538B";
+
+ if (firstInt)
+ {
+ firstInt = false;
+ return NumberParsing.decodeIntFromHex(x0);
+ }
+ return NumberParsing.decodeIntFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+
+ byte[] d = Hex.decode("02");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("ab8f37938356529e871514c1f48c5cbce77b2f4fc9a2673ac2c1653da8984090c0ac73775159a26bef59909d4c9846631270e16653a6234668f2a52a01a39b921490e694c0f104b58d2e14970fccb478f98d01e975a1028b9536d912de5236d2dd2fc396b77153594d4178780e5f16f718471e2111c8ce64a7d7e196fa57142d", 16);
+ BigInteger qValue = new BigInteger("bcc02ca0ce4f0753ec16105ee5d530aa00d39f3171842ab2c334a26b5f576e0f", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("a8790aabbd5a998ff524bad048ac69cd1faff2dab048265c8d60d1471c44a9ee",16);
+ BigInteger s = new BigInteger("30df5ba32ac77170b9632559bef7d37620017756dff3fea1088b4267db0944b8",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 1, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_TEST2_1024
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-TEST2-1024";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x3DFC46F1";
+ String c = "0xD";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+
+ byte[] d = Hex.decode("02");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("e2c4191c4b5f222f9ac2732562f6d9b4f18e7fb67a290ea1e03d750f0b9806755fc730d975bf3faa606d05c218b35a6c3706919aab92e0c58b1de4531c8fa8e7af43c2bff016251e21b2870897f6a27ac4450bca235a5b748ad386e4a0e4dfcb09152435abcfe48bd0b126a8122c7382f285a9864615c66decddf6afd355dfb7", 16);
+ BigInteger qValue = new BigInteger("931a58fb6f0dcdf2fe7549bc3f19f4724b56898f7f921a076601edb18c93dc75", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("81d69a192e9c7ac21fc07da41bd07e230ba6a94eb9f3c1fd104c7bd976733ca5",16);
+ BigInteger s = new BigInteger("315c879c8414f35feb4deb15e7cc0278c48e6ca1596325d6959338d860b0c47a",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_AParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-AParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x520874F5";
+ String c = "0xEE39ADB3";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+
+ byte[] d = Hex.decode("02");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("b4e25efb018e3c8b87505e2a67553c5edc56c2914b7e4f89d23f03f03377e70a2903489dd60e78418d3d851edb5317c4871e40b04228c3b7902963c4b7d85d52b9aa88f2afdbeb28da8869d6df846a1d98924e925561bd69300b9ddd05d247b5922d967cbb02671881c57d10e5ef72d3e6dad4223dc82aa1f7d0294651a480df", 16);
+ BigInteger qValue = new BigInteger("972432a437178b30bd96195b773789ab2fff15594b176dd175b63256ee5af2cf", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("64a8856628e5669d85f62cd763dd4a99bc56d33dc0e1859122855d141e9e4774",16);
+ BigInteger s = new BigInteger("319ebac97092b288d469a4b988248794f60c865bc97858d9a3135c6d1a1bf2dd",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_BParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-BParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x5B977CDB";
+ String c = "0x6E9692DD";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("bc3cbbdb7e6f848286e19ad9a27a8e297e5b71c53dd974cdf60f937356df69cbc97a300ccc71685c553046147f11568c4fddf363d9d886438345a62c3b75963d6546adfabf31b31290d12cae65ecb8309ef66782");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("c6971fc57524b30c9018c5e621de15499736854f56a6f8aee65a7a404632b3540f09020f67f04dc2e6783b141dceffd21a703035b7d0187c6e12cb4229922bafdb2225b73e6b23a0de36e20047065aea000c1a374283d0ad8dc1981e3995f0bb8c72526041fcb98ae6163e1e71a669d8364e9c4c3188f673c5f8ee6fadb41abf", 16);
+ BigInteger qValue = new BigInteger("b09d634c10899cd7d4c3a7657403e05810b07c61a688bab2c37f475e308b0607", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("860d82c60e9502cd00c0e9e1f6563feafec304801974d745c5e02079946f729e",16);
+ BigInteger s = new BigInteger("7ef49264ef022801aaa03033cd97915235fbab4c823ed936b0f360c22114688a",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_CParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-CParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x43848744";
+ String c = "0xB50A826D";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("7F575E8194BC5BDF");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("9d88e6d7fe3313bd2e745c7cdd2ab9ee4af3c8899e847de74a33783ea68bc30588ba1f738c6aaf8ab350531f1854c3837cc3c860ffd7e2e106c3f63b3d8a4c034ce73942a6c3d585b599cf695ed7a3c4a93b2b947b7157bb1a1c043ab41ec8566c6145e938a611906de0d32e562494569d7e999a0dda5c879bdd91fe124df1e9", 16);
+ BigInteger qValue = new BigInteger("fadd197abd19a1b4653eecf7eca4d6a22b1f7f893b641f901641fbb555354faf", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("4deb95a0b35e7ed7edebe9bef5a0f93739e16b7ff27fe794d989d0c13159cfbc",16);
+ BigInteger s = new BigInteger("e1d0d30345c24cfeb33efde3deee5fbbda78ddc822b719d860cd0ba1fb6bd43b",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_DParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-DParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x13DA8B9D";
+ String c = "0xA0E9DE4B";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+
+ byte[] d = Hex.decode("41ab97857f42614355d32db0b1069f109a4da283676c7c53a68185b4");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("80f102d32b0fd167d069c27a307adad2c466091904dbaa55d5b8cc7026f2f7a1919b890cb652c40e054e1e9306735b43d7b279eddf9102001cd9e1a831fe8a163eed89ab07cf2abe8242ac9dedddbf98d62cddd1ea4f5f15d3a42a6677bdd293b24260c0f27c0f1d15948614d567b66fa902baa11a69ae3bceadbb83e399c9b5", 16);
+ BigInteger qValue = new BigInteger("f0f544c418aac234f683f033511b65c21651a6078bda2d69bb9f732867502149", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("712592d285b792e33b8a9a11e8e6c4f512ddf0042972bbfd1abb0a93e8fc6f54",16);
+ BigInteger s = new BigInteger("2cf26758321258b130d5612111339f09ceb8668241f3482e38baa56529963f07",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_AExParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-AExParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0xD05E9F14";
+ String c = "0x46304C5F";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("35ab875399cda33c146ca629660e5a5e5c07714ca326db032dd6751995cdb90a612b9228932d8302704ec24a5def7739c5813d83");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("ca3b3f2eee9fd46317d49595a9e7518e6c63d8f4eb4d22d10d28af0b8839f079f8289e603b03530784b9bb5a1e76859e4850c670c7b71c0df84ca3e0d6c177fe9f78a9d8433230a883cd82a2b2b5c7a3306980278570cdb79bf01074a69c9623348824b0c53791d53c6a78cab69e1cfb28368611a397f50f541e16db348dbe5f", 16);
+ BigInteger qValue = new BigInteger("cae4d85f80c147704b0ca48e85fb00a9057aa4acc44668e17f1996d7152690d9", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("90892707282f433398488f19d31ac48523a8e2ded68944e0da91c6895ee7045e",16);
+ BigInteger s = new BigInteger("3be4620ee88f1ee8f9dd63c7d145b7e554839feeca125049118262ea4651e9de",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_BExParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-BExParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x7A007804";
+ String c = "0xD31A4FF7";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("7ec123d161477762838c2bea9dbdf33074af6d41d108a066a1e7a07ab3048de2");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("9286dbda91eccfc3060aa5598318e2a639f5ba90a4ca656157b2673fb191cd0589ee05f4cef1bd13508408271458c30851ce7a4ef534742bfb11f4743c8f787b11193ba304c0e6bca25701bf88af1cb9b8fd4711d89f88e32b37d95316541bf1e5dbb4989b3df13659b88c0f97a3c1087b9f2d5317d557dcd4afc6d0a754e279", 16);
+ BigInteger qValue = new BigInteger("c966e9b3b8b7cdd82ff0f83af87036c38f42238ec50a876cd390e43d67b6013f", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("8f79a582513df84dc247bcb624340cc0e5a34c4324a20ce7fe3ab8ff38a9db71",16);
+ BigInteger s = new BigInteger("7508d22fd6cbb45efd438cb875e43f137247088d0f54b29a7c91f68a65b5fa85",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ private class GOST3410_CExParam
+ implements Test
+ {
+ public String getName()
+ {
+ return "GOST3410-CExParam";
+ }
+
+ SecureRandom init_random = new SecureRandom()
+ {
+ boolean firstLong = true;
+
+ public long nextLong()
+ {
+ String x0 = "0x162AB910";
+ String c = "0x93F828D3";
+
+ if (firstLong)
+ {
+ firstLong = false;
+ return NumberParsing.decodeLongFromHex(x0);
+ }
+ return NumberParsing.decodeLongFromHex(c);
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] d = Hex.decode("ca82cce78a738bc46f103d53b9bf809745ec845e4f6da462606c51f60ecf302e31204b81");
+
+ System.arraycopy(d, 0, bytes, bytes.length-d.length, d.length);
+ }
+ };
+
+ SecureRandom random = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] k = Hex.decode("90F3A564439242F5186EBB224C8E223811B7105C64E4F5390807E6362DF4C72A");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - k.length); i += k.length)
+ {
+ System.arraycopy(k, 0, bytes, i, k.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(k, 0, bytes, i - k.length, bytes.length - (i - k.length));
+ }
+ else
+ {
+ System.arraycopy(k, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ SecureRandom keyRandom = new SecureRandom()
+ {
+ public void nextBytes(byte[] bytes)
+ {
+ byte[] x = Hex.decode("3036314538303830343630454235324435324234314132373832433138443046");
+
+ int i;
+
+ for (i = 0; i < (bytes.length - x.length); i += x.length)
+ {
+ System.arraycopy(x, 0, bytes, i, x.length);
+ }
+
+ if (i > bytes.length)
+ {
+ System.arraycopy(x, 0, bytes, i - x.length, bytes.length - (i - x.length));
+ }
+ else
+ {
+ System.arraycopy(x, 0, bytes, i, bytes.length - i);
+ }
+ }
+ };
+
+ BigInteger pValue = new BigInteger("b194036ace14139d36d64295ae6c50fc4b7d65d8b340711366ca93f383653908ee637be428051d86612670ad7b402c09b820fa77d9da29c8111a8496da6c261a53ed252e4d8a69a20376e6addb3bdcd331749a491a184b8fda6d84c31cf05f9119b5ed35246ea4562d85928ba1136a8d0e5a7e5c764ba8902029a1336c631a1d", 16);
+ BigInteger qValue = new BigInteger("96120477df0f3896628e6f4a88d83c93204c210ff262bccb7dae450355125259", 16);
+
+ public TestResult perform()
+ {
+ BigInteger r = new BigInteger("169fdb2dc09f690b71332432bfec806042e258fa9a21dafe73c6abfbc71407d9",16);
+ BigInteger s = new BigInteger("9002551808ae40d19f6f31fb67e4563101243cf07cffd5f2f8ff4c537b0c9866",16);
+ GOST3410ParametersGenerator pGen = new GOST3410ParametersGenerator();
+
+ pGen.init(1024, 2, init_random);
+
+ GOST3410Parameters params = pGen.generateParameters();
+
+ if (!pValue.equals(params.getP()) || !qValue.equals(params.getQ()))
+ {
+ return new SimpleTestResult(false, getName() + ": p or q wrong");
+ }
+
+ GOST3410KeyPairGenerator GOST3410KeyGen = new GOST3410KeyPairGenerator();
+ GOST3410KeyGenerationParameters genParam = new GOST3410KeyGenerationParameters(keyRandom, params);
+
+ GOST3410KeyGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = GOST3410KeyGen.generateKeyPair();
+
+ ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random);
+
+ GOST3410Signer GOST3410 = new GOST3410Signer();
+
+ GOST3410.init(true, param);
+
+ BigInteger[] sig = GOST3410.generateSignature(hashmessage);
+
+ if (!r.equals(sig[0]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": r component wrong." + System.getProperty("line.separator")
+ + " expecting: " + r.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[0].toString(16));
+ }
+
+ if (!s.equals(sig[1]))
+ {
+ return new SimpleTestResult(false, getName()
+ + ": s component wrong." + System.getProperty("line.separator")
+ + " expecting: " + s.toString(16) + System.getProperty("line.separator")
+ + " got : " + sig[1].toString(16));
+ }
+
+ GOST3410.init(false, pair.getPublic());
+
+ if (GOST3410.verifySignature(hashmessage, sig[0], sig[1]))
+ {
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, getName() + ": verification fails");
+ }
+ }
+ }
+
+ Test tests[] =
+ {
+ new GOST3410_TEST1_512(),
+ new GOST3410_TEST2_512(),
+// new GOST3410_TEST1_1024(),
+// new GOST3410_TEST2_1024(),
+// new GOST3410_AParam(),
+// new GOST3410_BParam(),
+// new GOST3410_CParam(),
+// new GOST3410_DParam(),
+// new GOST3410_AExParam(),
+// new GOST3410_BExParam(),
+// new GOST3410_CExParam()
+ };
+
+ public String getName()
+ {
+ return "GOST3410";
+ }
+
+ public TestResult perform()
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult result = tests[i].perform();
+
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+ }
+
+ return new SimpleTestResult(true, "GOST3410: Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ GOST3410Test test = new GOST3410Test();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3411DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3411DigestTest.java
new file mode 100644
index 00000000..45962fe7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/GOST3411DigestTest.java
@@ -0,0 +1,74 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.GOST3411Digest;
+import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+public class GOST3411DigestTest
+ extends DigestTest
+{
+ private static final String[] messages =
+ {
+ "",
+ "This is message, length=32 bytes",
+ "Suppose the original message has length = 50 bytes",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ };
+
+// If S-box = D-A (see: digest/GOST3411Digest.java; function: E(byte[] in, byte[] key); string: CipherParameters param = new GOST28147Parameters(key,"D-A");)
+ private static final String[] digests =
+ {
+ "981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0",
+ "2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb",
+ "c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011",
+ "73b70a39497de53a6e08c67b6d4db853540f03e9389299d9b0156ef7e85d0f61"
+ };
+
+// If S-box = D-Test (see: digest/GOST3411Digest.java; function:E(byte[] in, byte[] key); string: CipherParameters param = new GOST28147Parameters(key,"D-Test");)
+// private static final String[] digests =
+// {
+// "ce85b99cc46752fffee35cab9a7b0278abb4c2d2055cff685af4912c49490f8d",
+// "b1c466d37519b82e8319819ff32595e047a28cb6f83eff1c6916a815a637fffa",
+// "471aba57a60a770d3a76130635c1fbea4ef14de51f78b4ae57dd893b62f55208",
+// "95c1af627c356496d80274330b2cff6a10c67b5f597087202f94d06d2338cf8e"
+// };
+
+ // 1 million 'a'
+ static private String million_a_digest = "8693287aa62f9478f7cb312ec0866b6c4e4a0f11160441e8f4ffcd2715dd554f";
+
+ GOST3411DigestTest()
+ {
+ super(new GOST3411Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+
+ HMac gMac = new HMac(new GOST3411Digest());
+
+ gMac.init(new KeyParameter(PKCS5S1ParametersGenerator.PKCS5PasswordToUTF8Bytes("1".toCharArray())));
+
+ byte[] data = "fred".getBytes();
+
+ gMac.update(data, 0, data.length);
+ byte[] mac = new byte[gMac.getMacSize()];
+
+ gMac.doFinal(mac, 0);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new GOST3411Digest((GOST3411Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new GOST3411DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Grain128Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Grain128Test.java
new file mode 100644
index 00000000..afac2e0a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Grain128Test.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.Grain128Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Grain-128 Test
+ */
+public class Grain128Test
+ extends SimpleTest
+{
+
+ String keyStream1 = "f09b7bf7d7f6b5c2de2ffc73ac21397f";
+ String keyStream2 = "afb5babfa8de896b4b9c6acaf7c4fbfd";
+
+ public String getName()
+ {
+ return "Grain-128";
+ }
+
+ public void performTest()
+ {
+ Grain128Test1(new ParametersWithIV(new KeyParameter(Hex
+ .decode("00000000000000000000000000000000")), Hex
+ .decode("000000000000000000000000")));
+ Grain128Test2(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0123456789abcdef123456789abcdef0")), Hex
+ .decode("0123456789abcdef12345678")));
+ Grain128Test3(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0123456789abcdef123456789abcdef0")), Hex
+ .decode("0123456789abcdef12345678")));
+ }
+
+ private void Grain128Test1(CipherParameters params)
+ {
+ StreamCipher grain = new Grain128Engine();
+ byte[] in = new byte[16];
+ byte[] out = new byte[16];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream1)))
+ {
+ mismatch("Keystream 1", keyStream1, out);
+ }
+
+ grain.reset();
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream1)))
+ {
+ mismatch("Keystream 1", keyStream1, out);
+ }
+ }
+
+ private void Grain128Test2(CipherParameters params)
+ {
+ StreamCipher grain = new Grain128Engine();
+ byte[] in = new byte[16];
+ byte[] out = new byte[16];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream2)))
+ {
+ mismatch("Keystream 2", keyStream2, out);
+ }
+
+ grain.reset();
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream2)))
+ {
+ mismatch("Keystream 2", keyStream2, out);
+ }
+ }
+
+ private void Grain128Test3(CipherParameters params)
+ {
+ StreamCipher grain = new Grain128Engine();
+ byte[] in = "Encrypt me!".getBytes();
+ byte[] cipher = new byte[in.length];
+ byte[] clear = new byte[in.length];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, cipher, 0);
+ grain.reset();
+ grain.processBytes(cipher, 0, cipher.length, clear, 0);
+
+ if (!areEqual(in, clear))
+ {
+ mismatch("Test 3", new String(Hex.encode(in)), clear);
+ }
+ }
+
+ private void mismatch(String name, String expected, byte[] found)
+ {
+ fail("mismatch on " + name, expected, new String(Hex.encode(found)));
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new Grain128Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Grainv1Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Grainv1Test.java
new file mode 100644
index 00000000..b76c1f27
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Grainv1Test.java
@@ -0,0 +1,140 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.Grainv1Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Grain v1 Test
+ */
+public class Grainv1Test
+ extends SimpleTest
+{
+
+ String keyStream1 = "dee931cf1662a72f77d0";
+ String keyStream2 = "7f362bd3f7abae203664";
+ String keyStream4 = "017D13ECB20AE0C9ACF784CB06525F72"
+ + "CE6D52BEBB948F124668C35064559024"
+ + "49EEA505C19F3EE4D052C3D19DA9C4D1"
+ + "B92DBC7F07AFEA6A3D845DE60D8471FD";
+
+ public String getName()
+ {
+ return "Grain v1";
+ }
+
+ public void performTest()
+ {
+ Grainv1Test1(new ParametersWithIV(new KeyParameter(Hex
+ .decode("00000000000000000000")), Hex
+ .decode("0000000000000000")));
+ Grainv1Test2(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0123456789abcdef1234")), Hex
+ .decode("0123456789abcdef")));
+ Grainv1Test3(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0123456789abcdef1234")), Hex
+ .decode("0123456789abcdef")));
+ Grainv1Test4(new ParametersWithIV(new KeyParameter(Hex
+ .decode("0F62B5085BAE0154A7FA")), Hex
+ .decode("288FF65DC42B92F9")));
+ }
+
+ private void Grainv1Test1(CipherParameters params)
+ {
+ StreamCipher grain = new Grainv1Engine();
+ byte[] in = new byte[10];
+ byte[] out = new byte[10];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream1)))
+ {
+ mismatch("Keystream 1", keyStream1, out);
+ }
+
+ grain.reset();
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream1)))
+ {
+ mismatch("Keystream 1", keyStream1, out);
+ }
+ }
+
+ private void Grainv1Test2(CipherParameters params)
+ {
+ StreamCipher grain = new Grainv1Engine();
+ byte[] in = new byte[10];
+ byte[] out = new byte[10];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream2)))
+ {
+ mismatch("Keystream 2", keyStream2, out);
+ }
+
+ grain.reset();
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream2)))
+ {
+ mismatch("Keystream 2", keyStream2, out);
+ }
+ }
+
+ private void Grainv1Test3(CipherParameters params)
+ {
+ StreamCipher grain = new Grainv1Engine();
+ byte[] in = "Encrypt me!".getBytes();
+ byte[] cipher = new byte[in.length];
+ byte[] clear = new byte[in.length];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, cipher, 0);
+ grain.reset();
+ grain.processBytes(cipher, 0, cipher.length, clear, 0);
+
+ if (!areEqual(in, clear))
+ {
+ mismatch("Test 3", new String(Hex.encode(in)), clear);
+ }
+ }
+
+ private void Grainv1Test4(CipherParameters params)
+ {
+ StreamCipher grain = new Grainv1Engine();
+ byte[] in = new byte[keyStream4.length() / 2];
+ byte[] out = new byte[in.length];
+
+ grain.init(true, params);
+
+ grain.processBytes(in, 0, in.length, out, 0);
+
+ if (!areEqual(out, Hex.decode(keyStream4)))
+ {
+ mismatch("Keystream 4", keyStream4, out);
+ }
+ }
+
+ private void mismatch(String name, String expected, byte[] found)
+ {
+ fail("mismatch on " + name, expected, new String(Hex.encode(found)));
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new Grainv1Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/HCFamilyTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/HCFamilyTest.java
new file mode 100644
index 00000000..5604d9cc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/HCFamilyTest.java
@@ -0,0 +1,192 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.HC128Engine;
+import org.bouncycastle.crypto.engines.HC256Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * HC-128 and HC-256 Tests. Based on the test vectors in the official reference
+ * papers, respectively:
+ * <pre>
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
+ * </pre>
+ * See HCFamilyVecTest for a more exhaustive test based on the ecrypt vectors.
+ */
+public class HCFamilyTest
+ extends SimpleTest
+{
+ private static final byte[] MSG = new byte[64];
+
+ private static String[][] HC128_VerifiedTest =
+ {
+ {
+ "Set 2, vector# 0",
+ "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "82001573A003FD3B7FD72FFB0EAF63AA" +
+ "C62F12DEB629DCA72785A66268EC758B" +
+ "1EDB36900560898178E0AD009ABF1F49" +
+ "1330DC1C246E3D6CB264F6900271D59C"
+ },
+ {
+ "Set 6, vector# 0",
+ "0053A6F94C9FF24598EB3E91E4378ADD",
+ "0D74DB42A91077DE45AC137AE148AF16",
+ "2E1ED12A8551C05AF41FF39D8F9DF933" +
+ "122B5235D48FC2A6F20037E69BDBBCE8" +
+ "05782EFC16C455A4B3FF06142317535E" +
+ "F876104C32445138CB26EBC2F88A684C"
+ },
+ {
+ "Set 6, vector# 1",
+ "0558ABFE51A4F74A9DF04396E93C8FE2",
+ "167DE44BB21980E74EB51C83EA51B81F",
+ "4F864BF3C96D0363B1903F0739189138" +
+ "F6ED2BC0AF583FEEA0CEA66BA7E06E63" +
+ "FB28BF8B3CA0031D24ABB511C57DD17B" +
+ "FC2861C32400072CB680DF2E58A5CECC"
+ },
+ {
+ "Set 6, vector# 2",
+ "0A5DB00356A9FC4FA2F5489BEE4194E7",
+ "1F86ED54BB2289F057BE258CF35AC128",
+ "82168AB0023B79AAF1E6B4D823855E14" +
+ "A7084378036A951B1CFEF35173875ED8" +
+ "6CB66AB8410491A08582BE40080C3102" +
+ "193BA567F9E95D096C3CC60927DD7901"
+ },
+ {
+ "Set 6, vector# 3",
+ "0F62B5085BAE0154A7FA4DA0F34699EC",
+ "288FF65DC42B92F960C72E95FC63CA31",
+ "1CD8AEDDFE52E217E835D0B7E84E2922" +
+ "D04B1ADBCA53C4522B1AA604C42856A9" +
+ "0AF83E2614BCE65C0AECABDD8975B557" +
+ "00D6A26D52FFF0888DA38F1DE20B77B7"
+ }
+ };
+
+ private static String[][] HC256_VerifiedTest =
+ {
+ {
+ "Set 2, vector# 0",
+ "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "5B078985D8F6F30D42C5C02FA6B67951" +
+ "53F06534801F89F24E74248B720B4818" +
+ "CD9227ECEBCF4DBF8DBF6977E4AE14FA" +
+ "E8504C7BC8A9F3EA6C0106F5327E6981"
+ },
+ {
+ "Set 2, vector# 9",
+ "09090909090909090909090909090909",
+ "00000000000000000000000000000000",
+ "F5C2926651AEED9AF1A9C2F04C03D081" +
+ "2145B56AEA46EB283A25A4C9E3D8BEB4" +
+ "821B418F06F2B9DCDF1A85AB8C02CD14" +
+ "62E1BBCAEC9AB0E99AA6AFF918BA627C"
+ },
+ {
+ "Set 2, vector#135",
+ "87878787878787878787878787878787",
+ "00000000000000000000000000000000",
+ "CEC0C3852E3B98233EBCB975C10B1191" +
+ "3C69F2275EB97A1402EDF16C6FBE19BE" +
+ "79D65360445BCB63676E6553B609A065" +
+ "0155C3B22DD1975AC0F3F65063A2E16E"
+ },
+ {
+ "Set 6, vector# 0",
+ "0053A6F94C9FF24598EB3E91E4378ADD" +
+ "3083D6297CCF2275C81B6EC11467BA0D",
+ "0D74DB42A91077DE45AC137AE148AF16" +
+ "7DE44BB21980E74EB51C83EA51B81F86",
+ "23D9E70A45EB0127884D66D9F6F23C01" +
+ "D1F88AFD629270127247256C1FFF91E9" +
+ "1A797BD98ADD23AE15BEE6EEA3CEFDBF" +
+ "A3ED6D22D9C4F459DB10C40CDF4F4DFF"
+ },
+ {
+ "Set 6, vector# 1",
+ "0558ABFE51A4F74A9DF04396E93C8FE2" +
+ "3588DB2E81D4277ACD2073C6196CBF12",
+ "167DE44BB21980E74EB51C83EA51B81F" +
+ "86ED54BB2289F057BE258CF35AC1288F",
+ "C44B5262F2EAD9C018213127686DB742" +
+ "A72D3F2D61D18F0F4E7DE5B4F7ADABE0" +
+ "7E0C82033B139F02BAACB4E2F2D0BE30" +
+ "110C3A8A2B621523756692877C905DD0"
+ },
+ {
+ "Set 6, vector# 2",
+ "0A5DB00356A9FC4FA2F5489BEE4194E7" +
+ "3A8DE03386D92C7FD22578CB1E71C417",
+ "1F86ED54BB2289F057BE258CF35AC128" +
+ "8FF65DC42B92F960C72E95FC63CA3198",
+ "9D13AA06122F4F03AE60D507701F1ED0" +
+ "63D7530FF35EE76CAEDCBFB01D8A239E" +
+ "FA4A44B272DE9B4092E2AD56E87C3A60" +
+ "89F5A074D1F6E5B8FC6FABEE0C936F06"
+ },
+ {
+ "Set 6, vector# 3",
+ "0F62B5085BAE0154A7FA4DA0F34699EC" +
+ "3F92E5388BDE3184D72A7DD02376C91C",
+ "288FF65DC42B92F960C72E95FC63CA31" +
+ "98FF66CD349B0269D0379E056CD33AA1",
+ "C8632038DA61679C4685288B37D3E232" +
+ "7BC2D28C266B041FE0CA0D3CFEED8FD5" +
+ "753259BAB757168F85EA96ADABD823CA" +
+ "4684E918423E091565713FEDDE2CCFE0"
+ }
+ };
+
+ public String getName()
+ {
+ return "HC-128 and HC-256";
+ }
+
+ public void performTest()
+ {
+ StreamCipher hc = new HC256Engine();
+
+ for (int i = 0; i != HC256_VerifiedTest.length; i++)
+ {
+ String[] test = HC256_VerifiedTest[i];
+ HCTest(hc, "HC-256 - " + test[0], Hex.decode(test[1]), Hex.decode(test[2]), Hex.decode(test[3]));
+ }
+
+ hc = new HC128Engine();
+
+ for (int i = 0; i != HC128_VerifiedTest.length; i++)
+ {
+ String[] test = HC128_VerifiedTest[i];
+ HCTest(hc, "HC-128 - " + test[0], Hex.decode(test[1]), Hex.decode(test[2]), Hex.decode(test[3]));
+ }
+ }
+
+ private void HCTest(StreamCipher hc, String test, byte[] key, byte[] IV, byte[] expected)
+ {
+ KeyParameter kp = new KeyParameter(key);
+ ParametersWithIV ivp = new ParametersWithIV(kp, IV);
+
+ hc.init(true, ivp);
+ for (int i = 0; i < 64; i++)
+ {
+ if (hc.returnByte(MSG[i]) != expected[i])
+ {
+ fail(test + " failure at byte " + i);
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HCFamilyTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/HCFamilyVecTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/HCFamilyVecTest.java
new file mode 100644
index 00000000..51247e6a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/HCFamilyVecTest.java
@@ -0,0 +1,199 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.HC128Engine;
+import org.bouncycastle.crypto.engines.HC256Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * HC-128 and HC-256 Tests. Based on the test vectors in the official reference
+ * papers, respectively:
+ *
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf
+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
+ */
+public class HCFamilyVecTest
+ extends SimpleTest
+{
+ private static class PeekableLineReader extends BufferedReader
+ {
+ public PeekableLineReader(Reader r) throws IOException
+ {
+ super(r);
+
+ peek = super.readLine();
+ }
+
+ public String peekLine()
+ {
+ return peek;
+ }
+
+ public String readLine() throws IOException
+ {
+ String tmp = peek;
+ peek = super.readLine();
+ return tmp;
+ }
+
+ private String peek;
+ }
+
+ public String getName()
+ {
+ return "HC-128 and HC-256 (ecrypt)";
+ }
+
+ public void performTest() throws Exception
+ {
+ runTests(new HC128Engine(), "ecrypt_HC-128.txt");
+ runTests(new HC256Engine(), "ecrypt_HC-256_128K_128IV.txt");
+ runTests(new HC256Engine(), "ecrypt_HC-256_256K_128IV.txt");
+ runTests(new HC256Engine(), "ecrypt_HC-256_128K_256IV.txt");
+ runTests(new HC256Engine(), "ecrypt_HC-256_256K_256IV.txt");
+ }
+
+ private void runTests(StreamCipher hc, String fileName) throws IOException
+ {
+ Reader resource = new InputStreamReader(getClass().getResourceAsStream(fileName));
+ PeekableLineReader r = new PeekableLineReader(resource);
+ runAllVectors(hc, fileName, r);
+ }
+
+ private void runAllVectors(StreamCipher hc, String fileName, PeekableLineReader r)
+ throws IOException
+ {
+ for (;;)
+ {
+ String line = r.readLine();
+ if (line == null)
+ {
+ break;
+ }
+
+ line = line.trim();
+
+ if (line.startsWith("Set "))
+ {
+ runVector(hc, fileName, r, dellChar(line, ':'));
+ }
+ }
+ }
+
+ private String dellChar(String s, char c)
+ {
+ StringBuffer b = new StringBuffer();
+
+ for (int i = 0; i != s.length(); i++)
+ {
+ if (s.charAt(i) != c)
+ {
+ b.append(s.charAt(i));
+ }
+ }
+
+ return b.toString();
+ }
+
+ private void runVector(StreamCipher hc, String fileName, PeekableLineReader r, String vectorName)
+ throws IOException
+ {
+// System.out.println(fileName + " => " + vectorName);
+ String hexKey = readBlock(r);
+ String hexIV = readBlock(r);
+
+ CipherParameters cp = new KeyParameter(Hex.decode(hexKey));
+ cp = new ParametersWithIV(cp, Hex.decode(hexIV));
+ hc.init(true, cp);
+
+ byte[] input = new byte[64];
+ byte[] output = new byte[64];
+ byte[] digest = new byte[64];
+ int pos = 0;
+
+ for (;;)
+ {
+ String line1 = r.peekLine().trim();
+ int equalsPos = line1.indexOf('=');
+ String lead = line1.substring(0, equalsPos - 1);
+
+ String hexData = readBlock(r);
+ byte[] data = Hex.decode(hexData);
+
+ if (lead.equals("xor-digest"))
+ {
+ if (!Arrays.areEqual(data, digest))
+ {
+ fail("Failed in " + fileName + " for test vector: " + vectorName + " at " + lead);
+// System.out.println(fileName + " => " + vectorName + " failed at " + lead); return;
+ }
+ break;
+ }
+
+ int posA = lead.indexOf('[');
+ int posB = lead.indexOf("..");
+ int posC = lead.indexOf(']');
+ int start = Integer.parseInt(lead.substring(posA + 1, posB));
+ int end = Integer.parseInt(lead.substring(posB + 2, posC));
+
+ if (start % 64 != 0 || (end - start != 63))
+ {
+ throw new IllegalStateException(vectorName + ": " + lead + " not on 64 byte boundaries");
+ }
+
+ while (pos < end)
+ {
+ hc.processBytes(input, 0, input.length, output, 0);
+ xor(digest, output);
+ pos += 64;
+ }
+
+ if (!Arrays.areEqual(data, output))
+ {
+ fail("Failed in " + fileName + " for test vector: " + vectorName + " at " + lead);
+// System.out.println(fileName + " => " + vectorName + " failed at " + lead); return;
+ }
+ }
+ }
+
+ private static String readBlock(PeekableLineReader r) throws IOException
+ {
+ String first = r.readLine().trim();
+ String result = first.substring(first.lastIndexOf(' ') + 1);
+
+ for (;;)
+ {
+ String peek = r.peekLine().trim();
+ if (peek.length() < 1 || peek.indexOf('=') >= 0)
+ {
+ break;
+ }
+ result += r.readLine().trim();
+ }
+
+ return result;
+ }
+
+ private static void xor(byte[] digest, byte[] block)
+ {
+ for (int i = 0; i < digest.length; ++i)
+ {
+ digest[i] ^= block[i];
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HCFamilyVecTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/HKDFGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/HKDFGeneratorTest.java
new file mode 100644
index 00000000..95135174
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/HKDFGeneratorTest.java
@@ -0,0 +1,304 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
+import org.bouncycastle.crypto.params.HKDFParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * HKDF tests - vectors from RFC 5869, + 2 more, 101 and 102
+ */
+public class HKDFGeneratorTest
+ extends SimpleTest
+{
+
+ public HKDFGeneratorTest()
+ {
+ }
+
+ private void compareOKM(int test, byte[] calculatedOKM, byte[] testOKM)
+ {
+
+ if (!areEqual(calculatedOKM, testOKM))
+ {
+ fail("HKDF failed generator test " + test);
+ }
+ }
+
+ public void performTest()
+ {
+ {
+ // === A.1. Test Case 1 - Basic test case with SHA-256 ===
+
+ Digest hash = new SHA256Digest();
+ byte[] ikm = Hex
+ .decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
+ byte[] salt = Hex.decode("000102030405060708090a0b0c");
+ byte[] info = Hex.decode("f0f1f2f3f4f5f6f7f8f9");
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(1, okm, Hex.decode(
+ "3cb25f25faacd57a90434f64d0362f2a" +
+ "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" +
+ "34007208d5b887185865"));
+ }
+
+ // === A.2. Test Case 2 - Test with SHA-256 and longer inputs/outputs
+ // ===
+ {
+ Digest hash = new SHA256Digest();
+ byte[] ikm = Hex.decode("000102030405060708090a0b0c0d0e0f"
+ + "101112131415161718191a1b1c1d1e1f"
+ + "202122232425262728292a2b2c2d2e2f"
+ + "303132333435363738393a3b3c3d3e3f"
+ + "404142434445464748494a4b4c4d4e4f");
+ byte[] salt = Hex.decode("606162636465666768696a6b6c6d6e6f"
+ + "707172737475767778797a7b7c7d7e7f"
+ + "808182838485868788898a8b8c8d8e8f"
+ + "909192939495969798999a9b9c9d9e9f"
+ + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
+ byte[] info = Hex.decode("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+ int l = 82;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(2, okm, Hex.decode(
+ "b11e398dc80327a1c8e7f78c596a4934" +
+ "4f012eda2d4efad8a050cc4c19afa97c" +
+ "59045a99cac7827271cb41c65e590e09" +
+ "da3275600c2f09b8367793a9aca3db71" +
+ "cc30c58179ec3e87c14c01d5c1f3434f" +
+ "1d87"));
+ }
+
+ {
+ // === A.3. Test Case 3 - Test with SHA-256 and zero-length
+ // salt/info ===
+
+ // setting salt to an empty byte array means that the salt is set to
+ // HashLen zero valued bytes
+ // setting info to null generates an empty byte array as info
+ // structure
+
+ Digest hash = new SHA256Digest();
+ byte[] ikm = Hex
+ .decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
+ byte[] salt = new byte[0];
+ byte[] info = null;
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(3, okm, Hex.decode(
+ "8da4e775a563c18f715f802a063c5a31" +
+ "b8a11f5c5ee1879ec3454e5f3c738d2d" +
+ "9d201395faa4b61a96c8"));
+ }
+
+ {
+ // === A.4. Test Case 4 - Basic test case with SHA-1 ===
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex.decode("0b0b0b0b0b0b0b0b0b0b0b");
+ byte[] salt = Hex.decode("000102030405060708090a0b0c");
+ byte[] info = Hex.decode("f0f1f2f3f4f5f6f7f8f9");
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(4, okm, Hex.decode(
+ "085a01ea1b10f36933068b56efa5ad81" +
+ "a4f14b822f5b091568a9cdd4f155fda2" +
+ "c22e422478d305f3f896"));
+ }
+
+ // === A.5. Test Case 5 - Test with SHA-1 and longer inputs/outputs ===
+ {
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex.decode("000102030405060708090a0b0c0d0e0f"
+ + "101112131415161718191a1b1c1d1e1f"
+ + "202122232425262728292a2b2c2d2e2f"
+ + "303132333435363738393a3b3c3d3e3f"
+ + "404142434445464748494a4b4c4d4e4f");
+ byte[] salt = Hex.decode("606162636465666768696a6b6c6d6e6f"
+ + "707172737475767778797a7b7c7d7e7f"
+ + "808182838485868788898a8b8c8d8e8f"
+ + "909192939495969798999a9b9c9d9e9f"
+ + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
+ byte[] info = Hex.decode("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+ int l = 82;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(5, okm, Hex.decode(
+ "0bd770a74d1160f7c9f12cd5912a06eb" +
+ "ff6adcae899d92191fe4305673ba2ffe" +
+ "8fa3f1a4e5ad79f3f334b3b202b2173c" +
+ "486ea37ce3d397ed034c7f9dfeb15c5e" +
+ "927336d0441f4c4300e2cff0d0900b52" +
+ "d3b4"));
+ }
+
+ {
+ // === A.6. Test Case 6 - Test with SHA-1 and zero-length salt/info
+ // ===
+
+ // setting salt to null should generate a new salt of HashLen zero
+ // valued bytes
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex
+ .decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
+ byte[] salt = null;
+ byte[] info = new byte[0];
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(6, okm, Hex.decode(
+ "0ac1af7002b3d761d1e55298da9d0506" +
+ "b9ae52057220a306e07b6b87e8df21d0" +
+ "ea00033de03984d34918"));
+ }
+
+ {
+ // === A.7. Test Case 7 - Test with SHA-1, salt not provided,
+ // zero-length info ===
+ // (salt defaults to HashLen zero octets)
+
+ // this test is identical to test 6 in all ways bar the IKM value
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex
+ .decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c");
+ byte[] salt = null;
+ byte[] info = new byte[0];
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = new HKDFParameters(ikm, salt, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(7, okm, Hex.decode(
+ "2c91117204d745f3500d636a62f64f0a" +
+ "b3bae548aa53d423b0d1f27ebba6f5e5" +
+ "673a081d70cce7acfc48"));
+ }
+
+ {
+ // === A.101. Additional Test Case - Test with SHA-1, skipping extract
+ // zero-length info ===
+ // (salt defaults to HashLen zero octets)
+
+ // this test is identical to test 7 in all ways bar the IKM value
+ // which is set to the PRK value
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex
+ .decode("2adccada18779e7c2077ad2eb19d3f3e731385dd");
+ byte[] info = new byte[0];
+ int l = 42;
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = HKDFParameters.skipExtractParameters(ikm, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ compareOKM(101, okm, Hex.decode(
+ "2c91117204d745f3500d636a62f64f0a" +
+ "b3bae548aa53d423b0d1f27ebba6f5e5" +
+ "673a081d70cce7acfc48"));
+ }
+
+ // === A.102. Additional Test Case - Test with SHA-1, maximum output ===
+ // (salt defaults to HashLen zero octets)
+
+ // this test is identical to test 7 in all ways bar the IKM value
+
+ Digest hash = new SHA1Digest();
+ byte[] ikm = Hex
+ .decode("2adccada18779e7c2077ad2eb19d3f3e731385dd");
+ byte[] info = new byte[0];
+ int l = 255 * hash.getDigestSize();
+ byte[] okm = new byte[l];
+
+ HKDFParameters params = HKDFParameters.skipExtractParameters(ikm, info);
+
+ HKDFBytesGenerator hkdf = new HKDFBytesGenerator(hash);
+ hkdf.init(params);
+ hkdf.generateBytes(okm, 0, l);
+
+ int zeros = 0;
+ for (int i = 0; i < hash.getDigestSize(); i++)
+ {
+ if (okm[i] == 0)
+ {
+ zeros++;
+ }
+ }
+
+ if (zeros == hash.getDigestSize())
+ {
+ fail("HKDF failed generator test " + 102);
+ }
+ }
+
+ public String getName()
+ {
+ return "HKDF";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new HKDFGeneratorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/HashCommitmentTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/HashCommitmentTest.java
new file mode 100644
index 00000000..ed83c64e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/HashCommitmentTest.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Commitment;
+import org.bouncycastle.crypto.Committer;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.commitments.GeneralHashCommitter;
+import org.bouncycastle.crypto.commitments.HashCommitter;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class HashCommitmentTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "HashCommitmentTest";
+ }
+
+ public void performBasicTest()
+ throws Exception
+ {
+ byte[] data = Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20");
+
+ Committer committer = new HashCommitter(new SHA256Digest(), new SecureRandom());
+
+ Commitment c = committer.commit(data);
+
+ committer = new HashCommitter(new SHA256Digest(), new SecureRandom());
+
+ if (!committer.isRevealed(c, data))
+ {
+ fail("commitment failed to validate");
+ }
+
+ committer = new HashCommitter(new SHA1Digest(), new SecureRandom());
+
+ if (committer.isRevealed(c, data))
+ {
+ fail("commitment validated!!");
+ }
+
+ try
+ {
+ committer.isRevealed(c, new byte[data.length + 1]);
+ }
+ catch (Exception e)
+ {
+ if (!e.getMessage().equals("Message and witness secret lengths do not match."))
+ {
+ fail("exception thrown but wrong message");
+ }
+ }
+
+ // SHA1 has a block size of 512 bits, try a message that's too big
+
+ try
+ {
+ c = committer.commit(new byte[33]);
+ }
+ catch (DataLengthException e)
+ {
+ if (!e.getMessage().equals("Message to be committed to too large for digest."))
+ {
+ fail("exception thrown but wrong message");
+ }
+ }
+ }
+
+ public void performGeneralTest()
+ throws Exception
+ {
+ byte[] data = Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20");
+
+ Committer committer = new GeneralHashCommitter(new SHA256Digest(), new SecureRandom());
+
+ Commitment c = committer.commit(data);
+
+ committer = new GeneralHashCommitter(new SHA256Digest(), new SecureRandom());
+
+ if (!committer.isRevealed(c, data))
+ {
+ fail("general commitment failed to validate");
+ }
+
+ committer = new GeneralHashCommitter(new SHA1Digest(), new SecureRandom());
+
+ if (committer.isRevealed(c, data))
+ {
+ fail("general commitment validated!!");
+ }
+
+ c = committer.commit(data);
+
+ // try and fool it.
+ byte[] s = c.getSecret();
+ byte[] newS = Arrays.copyOfRange(s, 0, s.length - 1);
+ byte[] newData = new byte[data.length + 1];
+
+ newData[0] = s[s.length - 1];
+ System.arraycopy(data, 0, newData, 1, data.length);
+
+ c = new Commitment(newS, c.getCommitment());
+
+ if (committer.isRevealed(c, newData))
+ {
+ fail("general commitment validated!!");
+ }
+
+ try
+ {
+ committer.isRevealed(c, new byte[data.length + 1]);
+ }
+ catch (Exception e)
+ {
+ if (!e.getMessage().equals("Message and witness secret lengths do not match."))
+ {
+ fail("exception thrown but wrong message");
+ }
+ }
+
+ // SHA1 has a block size of 512 bits, try a message that's too big
+
+ try
+ {
+ c = committer.commit(new byte[33]);
+ }
+ catch (DataLengthException e)
+ {
+ if (!e.getMessage().equals("Message to be committed to too large for digest."))
+ {
+ fail("exception thrown but wrong message");
+ }
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ performBasicTest();
+ performGeneralTest();
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new HashCommitmentTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/IDEATest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/IDEATest.java
new file mode 100644
index 00000000..64c673ee
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/IDEATest.java
@@ -0,0 +1,38 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.IDEAEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ */
+public class IDEATest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new IDEAEngine(),
+ new KeyParameter(Hex.decode("00112233445566778899AABBCCDDEEFF")),
+ "000102030405060708090a0b0c0d0e0f", "ed732271a7b39f475b4b2b6719f194bf"),
+ new BlockCipherVectorTest(0, new IDEAEngine(),
+ new KeyParameter(Hex.decode("00112233445566778899AABBCCDDEEFF")),
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "b8bc6ed5c899265d2bcfad1fc6d4287d")
+ };
+
+ IDEATest()
+ {
+ super(tests, new IDEAEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "IDEA";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new IDEATest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ISAACTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ISAACTest.java
new file mode 100644
index 00000000..02319a38
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ISAACTest.java
@@ -0,0 +1,180 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.ISAACEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * ISAAC Test - see http://www.burtleburtle.net/bob/rand/isaacafa.html
+ */
+public class ISAACTest
+ extends SimpleTest
+{
+ byte[] out = Hex.decode(
+ "f650e4c8e448e96d98db2fb4f5fad54f433f1afbedec154ad837048746ca4f9a" +
+ "5de3743e88381097f1d444eb823cedb66a83e1e04a5f6355c744243325890e2e" +
+ "7452e31957161df638a824f3002ed71329f5544951c08d83d78cb99ea0cc74f3" +
+ "8f651659cbc8b7c2f5f71c6912ad6419e5792e1b860536b809b3ce98d45d6d81" +
+ "f3b2612917e38f8529cf72ce349947b0c998f9ffb5e13dae32ae2a2bf7cf814c" +
+ "8ebfa303cf22e0640b923200eca4d58aef53cec4d0f7b37d9c411a2affdf8a80" +
+ "b40e27bcb4d2f97644b89b08f37c71d51a70e7e90bdb9c3060dc5207b3c3f24b" +
+ "d7386806229749b54e232cd091dabc65a70e11018b87437e5781414fcdbc62e2" +
+ "8107c9ff69d2e4ae3b18e752b143b6886f4e077295138769943c3c74afc17a97" +
+ "0fd439636a529b0bd8c58a6aa8bcc22d2db35dfea7a2f4026cb167db538e1f4e" +
+ "7275e2771d3b8e97ecc5dc9115e3a5b90369661430ab93ecac9fe69d7bc76811" +
+ "60eda8da28833522d5295ebc5adb60e7f7e1cdd097166d14b67ec13a210f3925" +
+ "64af0fef0d0286843aea3decb058bafbb8b0ccfcf2b5cc05e3a662d9814bc24c" +
+ "2364a1aa37c0ed052b36505c451e7ec85d2a542fe43d0fbb91c8d92560d4d5f8" +
+ "12a0594b9e8a51dacd49ebdb1b0dcdc1cd57c7f7e63444517ded386f2f36fa86" +
+ "a6d1210133bc405db388d96cdb6dbe96fe29661c13edc0cbcb0eee4a70cc94ae" +
+ "de11ed340606cf9f3a6ce38923d74f4ea37f63ff917bdec2d73f72d40e7e0e67" +
+ "3d77d9a213add9228891b3db01a9bd7056a001e3d51f093dcc033ce35ad0d3b0" +
+ "34105a8c6a123f57bd2e50247364944be89b1a3b21835c4d9f39e2d9d405ded8" +
+ "294d37e5bccaaeed35a124b56708a2bcb00960ba2a98121a4d8fae820bb3263f" +
+ "12595a196a1075890809e49421c171ec884d682514c8009bb0b84e7b03fb88f4" +
+ "28e7cb789388b13bdd2dc1d5848f520a07c28cd168a3935872c9137d127dd430" +
+ "c613f1578c2f0d55f7d3f39f309bfb788406b13746c0a6f53718d59708607f04" +
+ "76904b6d04db4e13cd7411a7b510ce0ebfc7f7ccb83f957afdfef62dc35e4580" +
+ "3ff1e5244112d96c02c9b944d5990dfbe7e265810d9c7e7e826dfa8966f1e0ab" +
+ "30bcc764eadebeaced35e5ee0c571a7de4f3a26af7f58f7badf6bc235d023e65" +
+ "1ed3ff4eec46b0b6d2a93b51e75b41c97e315aeb61119a5a53245b7933f6d7b1" +
+ "cae8deba50fc8194afa92a6dc87c80064188bfcd8bace62e78ffa5685597ec0f" +
+ "b4415f7d08294766ad56764309c36f903dde9f394a0a283c18080c8e080c79ec" +
+ "79ae4c10cb9e15637cdd662f62d31911a4ca0cf15cf824cd3b708f991e16614c" +
+ "b6b9d7665de87abb7229ea81d5b2d75056e6cd21fe1e42d596da2655c2b9aa36" +
+ "b8f6fd4a6a158d1001913fd3af7d1fb80b5e435f90c107576554abda7a68710f" +
+ "82ac484fd7e1c7be95c85eaa94a302f44d3cfbda786b29081010b27582d53d12" +
+ "21e2a51c3d1e9150b059261dd0638e1a31860f0581f2864dff4cfc350451516d" +
+ "bd086f26bc5654c165dfa427a82427f5582e3014b8d2486dc79a17499a1d7745" +
+ "8766bb541e04a7f73d3dff8ad5ec6bf4dbef7d9f36ec0ea31feb2e4f15cfcc5c" +
+ "d8c423fbd0ef3cc9eb244925ba5590c8a5f48ac433c5321c613b67b2479c3a22" +
+ "e21339cc10d210aa931dd7e2ef05ee06b82f2703a385cb2c5d67133c877eb7b4" +
+ "1e3437f75afb43ae53c078f394d904811d96458908063a85e13222281956b1e5" +
+ "31860f132e7b022f21182ca396f703ac46819e2e0d28fe523724d4dca0eabe6b" +
+ "c66699fdc6112fdd19c1e69c04d3658a4b55dd9931907d62f854b5224d678f26" +
+ "22ae0582eafed133e4a51d2184bd6dd6c1a513753f28ee63fb737b1a70a1660e" +
+ "8a8dfaa31be79937f7476978513c1764531ac6bf12c06908001cdb951a4b6a53" +
+ "d067fce512b2cfb69ddb477f740e006639ddf25acc8bfa2df1b20eaf64f2632c" +
+ "9783cdee63bfd4d80084cfe575f4e9e219b48fd06c48ddd87a36af9371865c4c" +
+ "9ce0199d867027d72cb7b77f84ef01da72f5972f040f7074df9afa29c921f94e" +
+ "75c08a3618c1ef9ad649a428c5b719378a30738ad97cd348858129a6239e3b0a" +
+ "bbb8abc480fac4c2ecfcf20bd9d711f9e2a4ef71b5fe87c0be8b06b2aafef5a7" +
+ "9c15db3b0aeb81654389a84a253b1d7a19047c797cdc78a2d20adf0356f55a71" +
+ "3e730fa8fd8650d8959e234eb7546681dad1b22a142a6e858ef4bce668235b9d" +
+ "85a13f8574096ae7a949bea229322d0dd568385882846526403dae086dd1943a" +
+ "e1279bff9e7e4f041c3a4524484525e481d4cc5fe24124c0037464c0bf1bd691" +
+ "26ceb003275ead3ac5bde90826414ff3a30519add7b43abe2ce5d3d588412761" +
+ "97ca2070e5fbb9c7276df0b4308f751f37a97df6c9cd808cfe4cb3803d469303" +
+ "aee19096c0d5d42a4e823ad3f5f9cc3b4286619c9ca45e1c66c97340891aec49" +
+ "45bae606c798f04752649d6cce86fdfc80c6e402d6ec2f2b27c822821fe26ce0" +
+ "92f57ea7de462f4d07497cae5a48755c721502dd6cbe7935836d80039ead7f70" +
+ "9ab3a42f4c8652d632e39273e8fa38601da4f25a0cd6ef8102503f7d8854a0a1" +
+ "9a30c4e88815715305efe29457c4c9252887d96fc1a71e3ce9f841632d0985de" +
+ "d21e796c6fb5ce5602614abfc3c7be2cb54fed6fa617a083c3142d8f6079e4ce" +
+ "ceffc1471d0cb81bdc153e5fe36ef5bbd531161a165b10157aa114ed3f7579b3" +
+ "f7f395f1bc6172c7a86f875e0e6c51b3cdfec2af73c0e762824c2009c5a87748" +
+ "94d401258aba3ffbd32be0608c17eff021e2547e07cffad905340e15f3310c92" +
+ "9d8d190886ba527ff943f672ef73fbf046d95ca5c54cd95b9d855e894bb5af29");
+
+ byte[] outFFFFFFFF = Hex.decode(
+ "de3b3f3c19e0629c1fc8b7836695d523e7804edd86ff7ce9b106f52caebae9d9" +
+ "72f845d49ce17d7da44e49bae954aac0d0b1284b98a88eec1524fb6bc91a16b5" +
+ "1192ac5334131446ac2442de9ff3d5867b9b9148881ee30a6e87dd88e5d1f7cd" +
+ "98db31ff36f70d9850cfefaef42abb00ecc39ed308bf4b8030cdc2b6b7e42f0e" +
+ "908030dd282f96edacc888b3a986e109c129998f89baa1b5da8970b07a6ab012" +
+ "f10264f23c315c9c8e0c164955c68517b6a4f982b2626db70787f869ac6d551b" +
+ "e34931627c7058e965c502e18d2cd370e6db3b70d947d61aa9717cf8394f48c6" +
+ "3c796f3a154950846badb28b70d982f29bc670254e3e5e0f8e36b0a5f6da0a04" +
+ "6b235ed6a42988c012bde74d879fa8eb5d59f5f40ed5e76601c9847b3edb2690");
+
+ byte[] outFFFF0000 = Hex.decode(
+ "26c54b1f8c4e3fc582e9e8180f7aba5380463dcf58b03cbeda0ecc8ba90ccff8" +
+ "5bd50896313d7efed44015faeac6964b241a7fb8a2e37127a7cbea0fd7c020f2" +
+ "406371b87ef5185089504751e5e44352eff63e00e5c28f5dff0616a9a3a00f1f" +
+ "4a1350e3a17be9abddfc2c94571450a0dc4c3c0c7c7f98e80c95f607d50c676a" +
+ "9a3006f9d279a79a4d66b2ab0c52930c9ee84bc09895e70fa041b1a3a2966f11" +
+ "6a47fd09705124b1f5c7ae055e54536e66584b1608f3612d81b72f109a385831" +
+ "121945b207b90ac72437a248f27a121c2801f4153a8699fb047e193f7ba69e1b" +
+ "b117869675d4c963e6070c2ca3d332ce830cb5e3d9ed2eee7faf0acc20fbe154" +
+ "188ae789e95bd5c1f459dbd150aab6eb833170257084bc5d44e9df09f5624f9d" +
+ "afecd0c9340ac8587f8625d343f7efd1cc8abcf7a6f90eabd4e8e2d906278d6e" +
+ "431fcade165c8c467887fbf5c26d341557b064b98c60dd40ab262dc046d69647" +
+ "56f3ddc1a07ae5f87be878b9334fcde40add68d2ca1dc05fb1670f998c7c4607" +
+ "9a6e48bdb330ad8d30b61b5cc8dc156f5733905931949783f89ac396b65aa4b8" +
+ "51f746b53ed8ea66130e1d75e8eab136e60450e3e600226bc8e17d03744ce94c" +
+ "0eec9234fea5f18eef65d81f2f10cfbc0b112b8cde17c32eb33ed81d7356eac3" +
+ "eb1cb9cefa6604c2d707949b6e5a83e60705bf6aae76dcc7d35d68ff149c1ac5" +
+ "424bb4a39e2f496f886637fce3db4ba4ad12c1a32d25e1606f6635ff636486f6" +
+ "714997b45477f38813c02afce4bebf196b813332f0decd567c745f441e736364");
+
+ byte[] out0000FFFF = Hex.decode(
+ "bc31712f2a2f467a5abc737c57ce0f8d49d2f775eb850fc8f856daf19310fee2"+
+ "5bab40e78403c9ef4ccd971418992faf4e85ca643fa6b482f30c4659066158a6"+
+ "5bc3e620ba7ea5c34dd0eac5aabb2cf078d915fd1f8c437ed00423076c10f701"+
+ "eefa7fc7c461aca5db8a87be29d925c4212d4adcfa71ff5b06af15c048aa0dfd"+
+ "f0e645bc09fea200c430a88eb38c466ff358b836f1159656a078f6fc752f6db1"+
+ "6680bb30fc771a6a785bbb2298e947d7b3500e557775962248bedf4e82c16e66"+
+ "f39283ccb95e5399061056a11c4a280f00f7487888199487905273c7aa13012b"+
+ "4849eca626cbf071c782e084f9fded57de92313e5f61a6e81117fb1115eff275"+
+ "66fd5c755bb3b01bba69aeb8f1b1b1cc9709734be31b35bc707d372ba6fe70d1"+
+ "e2c3b0e5e74a7058faff6b11d3a168f19fecc9fcb36b3e6a5f828c01c22ac0c2"+
+ "5da2a3a9eec7e0ebbbf51472e430ed4cf1c7ab57ef9aea511e40250846d260b6"+
+ "17a3fdeba16cf4afaf700144d3296b58b22a3c79ed96f3e2fc8d9e3c660ae153"+
+ "8e0c285ccdc48b59117e80413bd0ad24c6a8d4f133fe1496f14351bb89904fa5"+
+ "e10c4b8d50e0604578389c336a9ab3d292beb90ce640fc028e697cf54e021e2f"+
+ "c0ca3fe0471fde5e5462f221739a74f5a13ae0621fe2a82e752bc294f63de48d"+
+ "e85430af71307a30441b861ab5380e6a6dbe1251c9baa567da14e38e5a0ccddf"+
+ "0127205c38fc3b77065e98101d219246103438d223ec7f8f533d4bb3a3d3407a"+
+ "944910f11e8e5492e86de7a0471250eca32f0838b3db02fffe71898712af3261");
+
+ public String getName()
+ {
+ return "ISAAC";
+ }
+
+ public void performTest()
+ {
+ ISAACEngine engine = new ISAACEngine();
+
+ doTest(engine, Hex.decode("00000000"), out);
+ doTest(engine, Hex.decode("ffffffff"), outFFFFFFFF);
+
+ byte[] k = new byte[256 * 4];
+ for (int i = 0; i != k.length; i++)
+ {
+ k[i] = (byte)((i % 4 == 0 || i % 4 == 1) ? 0xff : 0x00);
+ }
+ doTest(engine, k, outFFFF0000);
+ k = new byte[256 * 4];
+ for (int i = 0; i != k.length; i++)
+ {
+ k[i] = (byte)((i % 4 == 2 || i % 4 == 3) ? 0xff : 0x00);
+ }
+ doTest(engine, k, out0000FFFF);
+ }
+
+ private void doTest(ISAACEngine engine, byte[] key, byte[] output)
+ {
+ byte[] in = new byte[output.length];
+ byte[] enc = new byte[output.length];
+ engine.init(true, new KeyParameter(key));
+ engine.processBytes(in, 0, in.length, enc, 0);
+ if (!areEqual(enc, output))
+ {
+ fail("ciphertext mismatch");
+ }
+ engine.init(false, new KeyParameter(key));
+ engine.processBytes(enc, 0, enc.length, enc, 0);
+ if (!areEqual(enc, in))
+ {
+ fail("plaintext mismatch");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ISAACTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ISO9796Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ISO9796Test.java
new file mode 100644
index 00000000..88d41aaa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ISO9796Test.java
@@ -0,0 +1,972 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithSalt;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.signers.ISO9796d2PSSSigner;
+import org.bouncycastle.crypto.signers.ISO9796d2Signer;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * test vectors from ISO 9796-1 and ISO 9796-2 edition 1.
+ */
+public class ISO9796Test
+ extends SimpleTest
+{
+ static BigInteger mod1 = new BigInteger("0100000000000000000000000000000000bba2d15dbb303c8a21c5ebbcbae52b7125087920dd7cdf358ea119fd66fb064012ec8ce692f0a0b8e8321b041acd40b7", 16);
+
+ static BigInteger pub1 = new BigInteger("03", 16);
+
+ static BigInteger pri1 = new BigInteger("2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac9f0783a49dd5f6c5af651f4c9d0dc9281c96a3f16a85f9572d7cc3f2d0f25a9dbf1149e4cdc32273faadd3fda5dcda7", 16);
+
+ static BigInteger mod2 = new BigInteger("ffffff7fa27087c35ebead78412d2bdffe0301edd494df13458974ea89b364708f7d0f5a00a50779ddf9f7d4cb80b8891324da251a860c4ec9ef288104b3858d", 16);
+
+ static BigInteger pub2 = new BigInteger("03", 16);
+
+ static BigInteger pri2 = new BigInteger("2aaaaa9545bd6bf5e51fc7940adcdca5550080524e18cfd88b96e8d1c19de6121b13fac0eb0495d47928e047724d91d1740f6968457ce53ec8e24c9362ce84b5", 16);
+
+ static byte msg1[] = Hex.decode("0cbbaa99887766554433221100");
+
+ //
+ // you'll need to see the ISO 9796 to make sense of this
+ //
+ static byte sig1[] = mod1.subtract(new BigInteger("309f873d8ded8379490f6097eaafdabc137d3ebfd8f25ab5f138d56a719cdc526bdd022ea65dabab920a81013a85d092e04d3e421caab717c90d89ea45a8d23a", 16)).toByteArray();
+
+ static byte msg2[] = Hex.decode("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
+
+ static byte sig2[] = new BigInteger("319bb9becb49f3ed1bca26d0fcf09b0b0a508e4d0bd43b350f959b72cd25b3af47d608fdcd248eada74fbe19990dbeb9bf0da4b4e1200243a14e5cab3f7e610c", 16).toByteArray();
+
+ static byte msg3[] = Hex.decode("0112233445566778899aabbccd");
+
+ static byte sig3[] = mod2.subtract(new BigInteger("58e59ffb4b1fb1bcdbf8d1fe9afa3730c78a318a1134f5791b7313d480ff07ac319b068edf8f212945cb09cf33df30ace54f4a063fcca0b732f4b662dc4e2454", 16)).toByteArray();
+
+ //
+ // ISO 9796-2
+ //
+ static BigInteger mod3 = new BigInteger("ffffffff78f6c55506c59785e871211ee120b0b5dd644aa796d82413a47b24573f1be5745b5cd9950f6b389b52350d4e01e90009669a8720bf265a2865994190a661dea3c7828e2e7ca1b19651adc2d5", 16);
+
+ static BigInteger pub3 = new BigInteger("03", 16);
+
+ static BigInteger pri3 = new BigInteger("2aaaaaaa942920e38120ee965168302fd0301d73a4e60c7143ceb0adf0bf30b9352f50e8b9e4ceedd65343b2179005b2f099915e4b0c37e41314bb0821ad8330d23cba7f589e0f129b04c46b67dfce9d", 16);
+
+ static BigInteger mod4 = new BigInteger("FFFFFFFF45f1903ebb83d4d363f70dc647b839f2a84e119b8830b2dec424a1ce0c9fd667966b81407e89278283f27ca8857d40979407fc6da4cc8a20ecb4b8913b5813332409bc1f391a94c9c328dfe46695daf922259174544e2bfbe45cc5cd", 16);
+ static BigInteger pub4 = new BigInteger("02", 16);
+ static BigInteger pri4 = new BigInteger("1fffffffe8be3207d7707a9a6c7ee1b8c8f7073e5509c2337106165bd8849439c193faccf2cd70280fd124f0507e4f94cb66447680c6b87b6599d1b61c8f3600854a618262e9c1cb1438e485e47437be036d94b906087a61ee74ab0d9a1accd8", 16);
+
+ static byte msg4[] = Hex.decode("6162636462636465636465666465666765666768666768696768696a68696a6b696a6b6c6a6b6c6d6b6c6d6e6c6d6e6f6d6e6f706e6f7071");
+ static byte sig4[] = Hex.decode("374695b7ee8b273925b4656cc2e008d41463996534aa5aa5afe72a52ffd84e118085f8558f36631471d043ad342de268b94b080bee18a068c10965f581e7f32899ad378835477064abed8ef3bd530fce");
+
+ static byte msg5[] = Hex.decode("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
+ static byte sig5[] = Hex.decode("5cf9a01854dbacaec83aae8efc563d74538192e95466babacd361d7c86000fe42dcb4581e48e4feb862d04698da9203b1803b262105104d510b365ee9c660857ba1c001aa57abfd1c8de92e47c275cae");
+
+ //
+ // scheme 2 data
+ //
+ static BigInteger mod6 = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
+ static BigInteger pub6 = new BigInteger("11", 16);
+ static BigInteger pri6 = new BigInteger("92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619", 16);
+
+ static byte sig6[] = new BigInteger("0073FEAF13EB12914A43FE635022BB4AB8188A8F3ABD8D8A9E4AD6C355EE920359C7F237AE36B1212FE947F676C68FE362247D27D1F298CA9302EB21F4A64C26CE44471EF8C0DFE1A54606F0BA8E63E87CDACA993BFA62973B567473B4D38FAE73AB228600934A9CC1D3263E632E21FD52D2B95C5F7023DA63DE9509C01F6C7BBC", 16).modPow(pri6, mod6).toByteArray();
+
+ static byte msg7[] = Hex.decode("6162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F70716F70717270717273");
+ static byte sig7[] = new BigInteger("296B06224010E1EC230D4560A5F88F03550AAFCE31C805CE81E811E5E53E5F71AE64FC2A2A486B193E87972D90C54B807A862F21A21919A43ECF067240A8C8C641DE8DCDF1942CF790D136728FFC0D98FB906E7939C1EC0E64C0E067F0A7443D6170E411DF91F797D1FFD74009C4638462E69D5923E7433AEC028B9A90E633CC", 16).modPow(pri6, mod6).toByteArray();
+
+ static byte msg8[] = Hex.decode("FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA98");
+ static byte sig8[] = new BigInteger("01402B29ABA104079677CE7FC3D5A84DB24494D6F9508B4596484F5B3CC7E8AFCC4DDE7081F21CAE9D4F94D6D2CCCB43FCEDA0988FFD4EF2EAE72CFDEB4A2638F0A34A0C49664CD9DB723315759D758836C8BA26AC4348B66958AC94AE0B5A75195B57ABFB9971E21337A4B517F2E820B81F26BCE7C66F48A2DB12A8F3D731CC", 16).modPow(pri6, mod6).toByteArray();
+
+ static byte msg9[] = Hex.decode("6162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F70716F707172707172737172737472737475737475767475767775767778767778797778797A78797A61797A61627A6162636162636462636465");
+ static byte sig9[] = new BigInteger("6F2BB97571FE2EF205B66000E9DD06656655C1977F374E8666D636556A5FEEEEAF645555B25F45567C4EE5341F96FED86508C90A9E3F11B26E8D496139ED3E55ECE42860A6FB3A0817DAFBF13019D93E1D382DA07264FE99D9797D2F0B7779357CA7E74EE440D8855B7DDF15F000AC58EE3FFF144845E771907C0C83324A6FBC", 16).modPow(pri6, mod6).toByteArray();
+
+ public String getName()
+ {
+ return "ISO9796";
+ }
+
+ private boolean isSameAs(
+ byte[] a,
+ int off,
+ byte[] b)
+ {
+ if ((a.length - off) != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != b.length; i++)
+ {
+ if (a[i + off] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean startsWith(
+ byte[] a,
+ byte[] b)
+ {
+ if (a.length < b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != b.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void doTest1()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod1, pub1);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod1, pri1);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-1 - public encrypt, private decrypt
+ //
+ ISO9796d1Encoding eng = new ISO9796d1Encoding(rsa);
+
+ eng.init(true, privParameters);
+
+ eng.setPadBits(4);
+
+ data = eng.processBlock(msg1, 0, msg1.length);
+
+ eng.init(false, pubParameters);
+
+ if (!areEqual(sig1, data))
+ {
+ fail("failed ISO9796-1 generation Test 1");
+ }
+
+ data = eng.processBlock(data, 0, data.length);
+
+ if (!areEqual(msg1, data))
+ {
+ fail("failed ISO9796-1 retrieve Test 1");
+ }
+ }
+
+ private void doTest2()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod1, pub1);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod1, pri1);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-1 - public encrypt, private decrypt
+ //
+ ISO9796d1Encoding eng = new ISO9796d1Encoding(rsa);
+
+ eng.init(true, privParameters);
+
+ data = eng.processBlock(msg2, 0, msg2.length);
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(data, 1, sig2))
+ {
+ fail("failed ISO9796-1 generation Test 2");
+ }
+
+ data = eng.processBlock(data, 0, data.length);
+
+
+ if (!areEqual(msg2, data))
+ {
+ fail("failed ISO9796-1 retrieve Test 2");
+ }
+ }
+
+ public void doTest3()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod2, pub2);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod2, pri2);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-1 - public encrypt, private decrypt
+ //
+ ISO9796d1Encoding eng = new ISO9796d1Encoding(rsa);
+
+ eng.init(true, privParameters);
+
+ eng.setPadBits(4);
+
+ data = eng.processBlock(msg3, 0, msg3.length);
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig3, 1, data))
+ {
+ fail("failed ISO9796-1 generation Test 3");
+ }
+
+ data = eng.processBlock(data, 0, data.length);
+
+ if (!isSameAs(msg3, 0, data))
+ {
+ fail("failed ISO9796-1 retrieve Test 3");
+ }
+ }
+
+ public void doTest4()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod3, pub3);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod3, pri3);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - Signing
+ //
+ ISO9796d2Signer eng = new ISO9796d2Signer(rsa, new RIPEMD128Digest());
+
+ eng.init(true, privParameters);
+
+ eng.update(msg4[0]);
+ eng.update(msg4, 1, msg4.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig4, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 4");
+ }
+
+ eng.update(msg4[0]);
+ eng.update(msg4, 1, msg4.length - 1);
+
+ if (!eng.verifySignature(sig4))
+ {
+ fail("failed ISO9796-2 verify Test 4");
+ }
+
+ if (eng.hasFullMessage())
+ {
+ eng = new ISO9796d2Signer(rsa, new RIPEMD128Digest());
+
+ eng.init(false, pubParameters);
+
+ if (!eng.verifySignature(sig4))
+ {
+ fail("failed ISO9796-2 verify and recover Test 4");
+ }
+
+ if (!isSameAs(eng.getRecoveredMessage(), 0, msg4))
+ {
+ fail("failed ISO9796-2 recovered message Test 4");
+ }
+
+ // try update with recovered
+ eng.updateWithRecoveredMessage(sig4);
+
+ if (!isSameAs(eng.getRecoveredMessage(), 0, msg4))
+ {
+ fail("failed ISO9796-2 updateWithRecovered recovered message Test 4");
+ }
+
+ if (!eng.verifySignature(sig4))
+ {
+ fail("failed ISO9796-2 updateWithRecovered verify and recover Test 4");
+ }
+
+ if (!isSameAs(eng.getRecoveredMessage(), 0, msg4))
+ {
+ fail("failed ISO9796-2 updateWithRecovered recovered verify message Test 4");
+ }
+
+ // should fail
+ eng.updateWithRecoveredMessage(sig4);
+
+ eng.update(msg4, 0, msg4.length);
+
+ if (eng.verifySignature(sig4))
+ {
+ fail("failed ISO9796-2 updateWithRecovered verify and recover Test 4");
+ }
+ }
+ else
+ {
+ fail("full message flag false - Test 4");
+ }
+ }
+
+ public void doTest5()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod3, pub3);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod3, pri3);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - Signing
+ //
+ ISO9796d2Signer eng = new ISO9796d2Signer(rsa, new RIPEMD160Digest(), true);
+
+ eng.init(true, privParameters);
+
+ eng.update(msg5[0]);
+ eng.update(msg5, 1, msg5.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig5, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 5");
+ }
+
+ eng.update(msg5[0]);
+ eng.update(msg5, 1, msg5.length - 1);
+
+ if (!eng.verifySignature(sig5))
+ {
+ fail("failed ISO9796-2 verify Test 5");
+ }
+
+ if (eng.hasFullMessage())
+ {
+ fail("fullMessage true - Test 5");
+ }
+
+ if (!startsWith(msg5, eng.getRecoveredMessage()))
+ {
+ fail("failed ISO9796-2 partial recovered message Test 5");
+ }
+
+ int length = eng.getRecoveredMessage().length;
+
+ if (length >= msg5.length)
+ {
+ fail("Test 5 recovered message too long");
+ }
+
+ eng = new ISO9796d2Signer(rsa, new RIPEMD160Digest(), true);
+
+ eng.init(false, pubParameters);
+
+ eng.updateWithRecoveredMessage(sig5);
+
+ if (!startsWith(msg5, eng.getRecoveredMessage()))
+ {
+ fail("failed ISO9796-2 updateWithRecovered partial recovered message Test 5");
+ }
+
+ if (eng.hasFullMessage())
+ {
+ fail("fullMessage updateWithRecovered true - Test 5");
+ }
+
+ for (int i = length; i != msg5.length; i++)
+ {
+ eng.update(msg5[i]);
+ }
+
+ if (!eng.verifySignature(sig5))
+ {
+ fail("failed ISO9796-2 verify Test 5");
+ }
+
+ if (eng.hasFullMessage())
+ {
+ fail("fullMessage updateWithRecovered true - Test 5");
+ }
+
+ // should fail
+ eng.updateWithRecoveredMessage(sig5);
+
+ eng.update(msg5, 0, msg5.length);
+
+ if (eng.verifySignature(sig5))
+ {
+ fail("failed ISO9796-2 updateWithRecovered verify fail Test 5");
+ }
+ }
+
+ //
+ // against a zero length string
+ //
+
+ public void doTest6()
+ throws Exception
+ {
+ byte[] salt = Hex.decode("61DF870C4890FE85D6E3DD87C3DCE3723F91DB49");
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod6, pub6);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod6, pri6);
+ ParametersWithSalt sigParameters = new ParametersWithSalt(privParameters, salt);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, new RIPEMD160Digest(), 20, true);
+
+ eng.init(true, sigParameters);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig6, 1, data))
+ {
+ fail("failed ISO9796-2 generation Test 6");
+ }
+
+ if (!eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 verify Test 6");
+ }
+ }
+
+ public void doTest7()
+ throws Exception
+ {
+ byte[] salt = new byte[0];
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod6, pub6);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod6, pri6);
+ ParametersWithSalt sigParameters = new ParametersWithSalt(privParameters, salt);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, new SHA1Digest(), 0, false);
+
+ eng.init(true, sigParameters);
+
+ eng.update(msg7[0]);
+ eng.update(msg7, 1, msg7.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig7, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 7");
+ }
+
+ eng.update(msg7[0]);
+ eng.update(msg7, 1, msg7.length - 1);
+
+ if (!eng.verifySignature(sig7))
+ {
+ fail("failed ISO9796-2 verify Test 7");
+ }
+
+ if (!isSameAs(msg7, 0, eng.getRecoveredMessage()))
+ {
+ fail("failed ISO9796-2 recovery Test 7");
+ }
+ }
+
+ public void doTest8()
+ throws Exception
+ {
+ byte[] salt = Hex.decode("78E293203CBA1B7F92F05F4D171FF8CA3E738FF8");
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod6, pub6);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod6, pri6);
+ ParametersWithSalt sigParameters = new ParametersWithSalt(privParameters, salt);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, new RIPEMD160Digest(), 20, false);
+
+ eng.init(true, sigParameters);
+
+ eng.update(msg8[0]);
+ eng.update(msg8, 1, msg8.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig8, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 8");
+ }
+
+ eng.update(msg8[0]);
+ eng.update(msg8, 1, msg8.length - 1);
+
+ if (!eng.verifySignature(sig8))
+ {
+ fail("failed ISO9796-2 verify Test 8");
+ }
+ }
+
+ public void doTest9()
+ throws Exception
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod6, pub6);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod6, pri6);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, new RIPEMD160Digest(), 0, true);
+
+ eng.init(true, privParameters);
+
+ eng.update(msg9[0]);
+ eng.update(msg9, 1, msg9.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ if (!isSameAs(sig9, 0, data))
+ {
+ fail("failed ISO9796-2 generation Test 9");
+ }
+
+ eng.update(msg9[0]);
+ eng.update(msg9, 1, msg9.length - 1);
+
+ if (!eng.verifySignature(sig9))
+ {
+ fail("failed ISO9796-2 verify Test 9");
+ }
+ }
+
+ public void doTest10()
+ throws Exception
+ {
+ BigInteger mod = new BigInteger("B3ABE6D91A4020920F8B3847764ECB34C4EB64151A96FDE7B614DC986C810FF2FD73575BDF8532C06004C8B4C8B64F700A50AEC68C0701ED10E8D211A4EA554D", 16);
+ BigInteger pubExp = new BigInteger("65537", 10);
+ BigInteger priExp = new BigInteger("AEE76AE4716F77C5782838F328327012C097BD67E5E892E75C1356E372CCF8EE1AA2D2CBDFB4DA19F703743F7C0BA42B2D69202BA7338C294D1F8B6A5771FF41", 16);
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod, priExp);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ Digest dig = new SHA1Digest();
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, dig, dig.getDigestSize());
+
+ //
+ // as the padding is random this test needs to repeat a few times to
+ // make sure
+ //
+ for (int i = 0; i != 500; i++)
+ {
+ eng.init(true, privParameters);
+
+ eng.update(msg9[0]);
+ eng.update(msg9, 1, msg9.length - 1);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ eng.update(msg9[0]);
+ eng.update(msg9, 1, msg9.length - 1);
+
+ if (!eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 verify Test 10");
+ }
+ }
+ }
+
+ public void doTest11()
+ throws Exception
+ {
+ BigInteger mod = new BigInteger("B3ABE6D91A4020920F8B3847764ECB34C4EB64151A96FDE7B614DC986C810FF2FD73575BDF8532C06004C8B4C8B64F700A50AEC68C0701ED10E8D211A4EA554D", 16);
+ BigInteger pubExp = new BigInteger("65537", 10);
+ BigInteger priExp = new BigInteger("AEE76AE4716F77C5782838F328327012C097BD67E5E892E75C1356E372CCF8EE1AA2D2CBDFB4DA19F703743F7C0BA42B2D69202BA7338C294D1F8B6A5771FF41", 16);
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod, priExp);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+ byte[] m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ byte[] m2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
+ byte[] m3 = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ //
+ // ISO 9796-2 - PSS Signing
+ //
+ Digest dig = new SHA1Digest();
+ ISO9796d2PSSSigner eng = new ISO9796d2PSSSigner(rsa, dig, dig.getDigestSize());
+
+ //
+ // check message bounds
+ //
+ eng.init(true, privParameters);
+
+ eng.update(m1, 0, m1.length);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ eng.update(m2, 0, m2.length);
+
+ if (eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 m2 verify Test 11");
+ }
+
+ eng.init(false, pubParameters);
+
+ eng.update(m3, 0, m3.length);
+
+ if (eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 m3 verify Test 11");
+ }
+
+ eng.init(false, pubParameters);
+
+ eng.update(m1, 0, m1.length);
+
+ if (!eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 verify Test 11");
+ }
+ }
+
+ public void doTest12()
+ throws Exception
+ {
+ BigInteger mod = new BigInteger("B3ABE6D91A4020920F8B3847764ECB34C4EB64151A96FDE7B614DC986C810FF2FD73575BDF8532C06004C8B4C8B64F700A50AEC68C0701ED10E8D211A4EA554D", 16);
+ BigInteger pubExp = new BigInteger("65537", 10);
+ BigInteger priExp = new BigInteger("AEE76AE4716F77C5782838F328327012C097BD67E5E892E75C1356E372CCF8EE1AA2D2CBDFB4DA19F703743F7C0BA42B2D69202BA7338C294D1F8B6A5771FF41", 16);
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAKeyParameters(true, mod, priExp);
+ RSAEngine rsa = new RSAEngine();
+ byte[] data;
+ byte[] m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ byte[] m2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
+ byte[] m3 = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ //
+ // ISO 9796-2 - Signing
+ //
+ Digest dig = new SHA1Digest();
+ ISO9796d2Signer eng = new ISO9796d2Signer(rsa, dig);
+
+ //
+ // check message bounds
+ //
+ eng.init(true, privParameters);
+
+ eng.update(m1, 0, m1.length);
+
+ data = eng.generateSignature();
+
+ eng.init(false, pubParameters);
+
+ eng.update(m2, 0, m2.length);
+
+ if (eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 m2 verify Test 12");
+ }
+
+ eng.init(false, pubParameters);
+
+ eng.update(m3, 0, m3.length);
+
+ if (eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 m3 verify Test 12");
+ }
+
+ eng.init(false, pubParameters);
+
+ eng.update(m1, 0, m1.length);
+
+ if (!eng.verifySignature(data))
+ {
+ fail("failed ISO9796-2 verify Test 12");
+ }
+ }
+
+ private void doTest13()
+ throws Exception
+ {
+ BigInteger modulus = new BigInteger(1, Hex.decode("CDCBDABBF93BE8E8294E32B055256BBD0397735189BF75816341BB0D488D05D627991221DF7D59835C76A4BB4808ADEEB779E7794504E956ADC2A661B46904CDC71337DD29DDDD454124EF79CFDD7BC2C21952573CEFBA485CC38C6BD2428809B5A31A898A6B5648CAA4ED678D9743B589134B7187478996300EDBA16271A861"));
+ BigInteger pubExp = new BigInteger(1, Hex.decode("010001"));
+ BigInteger privExp = new BigInteger(1, Hex.decode("4BA6432AD42C74AA5AFCB6DF60FD57846CBC909489994ABD9C59FE439CC6D23D6DE2F3EA65B8335E796FD7904CA37C248367997257AFBD82B26F1A30525C447A236C65E6ADE43ECAAF7283584B2570FA07B340D9C9380D88EAACFFAEEFE7F472DBC9735C3FF3A3211E8A6BBFD94456B6A33C17A2C4EC18CE6335150548ED126D"));
+
+ RSAKeyParameters pubParams = new RSAKeyParameters(false, modulus, pubExp);
+ RSAKeyParameters privParams = new RSAKeyParameters(true, modulus, privExp);
+
+ AsymmetricBlockCipher rsaEngine = new RSABlindedEngine();
+ Digest digest = new SHA256Digest();
+
+ // set challenge to all zero's for verification
+ byte[] challenge = new byte[8];
+
+ // DOES NOT USE FINAL BOOLEAN TO INDICATE RECOVERY
+ ISO9796d2Signer signer = new ISO9796d2Signer(rsaEngine, digest, false);
+
+ // sign
+ signer.init(true, privParams);
+ signer.update(challenge, 0, challenge.length);
+
+ byte[] sig = signer.generateSignature();
+
+ // verify
+ signer.init(false, pubParams);
+ signer.update(challenge, 0, challenge.length);
+
+ if (!signer.verifySignature(sig))
+ {
+ fail("basic verification failed");
+ }
+
+ // === LETS ACTUALLY DO SOME RECOVERY, USING INPUT FROM INTERNAL AUTHENTICATE ===
+
+ signer.reset();
+
+ final String args0 = "482E20D1EDDED34359C38F5E7C01203F9D6B2641CDCA5C404D49ADAEDE034C7481D781D043722587761C90468DE69C6585A1E8B9C322F90E1B580EEDAB3F6007D0C366CF92B4DB8B41C8314929DCE2BE889C0129123484D2FD3D12763D2EBFD12AC8E51D7061AFCA1A53DEDEC7B9A617472A78C952CCC72467AE008E5F132994";
+
+ digest = new SHA1Digest();
+
+ signer = new ISO9796d2Signer(rsaEngine, digest, true);
+
+
+ signer.init(false, pubParams);
+ final byte[] signature = Hex.decode(args0);
+ signer.updateWithRecoveredMessage(signature);
+ signer.update(challenge, 0, challenge.length);
+
+ if (!signer.verifySignature(signature))
+ {
+ fail("recovered + challenge signature failed");
+ }
+
+ // === FINALLY, USING SHA-256 ===
+
+ signer.reset();
+
+ digest = new SHA256Digest();
+
+ // NOTE setting implit to false does not actually do anything for verification !!!
+ signer = new ISO9796d2Signer(rsaEngine, digest, false);
+
+
+ signer.init(true, privParams);
+ // generate NONCE of correct length using some inner knowledge
+ int nonceLength = modulus.bitLength() / 8 - 1 - digest.getDigestSize() - 2;
+ final byte[] nonce = new byte[nonceLength];
+ SecureRandom rnd = new SecureRandom();
+
+ rnd.nextBytes(nonce);
+
+ signer.update(nonce, 0, nonce.length);
+ signer.update(challenge, 0, challenge.length);
+ byte[] sig3 = signer.generateSignature();
+
+ signer.init(false, pubParams);
+ signer.updateWithRecoveredMessage(sig3);
+ signer.update(challenge, 0, challenge.length);
+ if (signer.verifySignature(sig3))
+ {
+ if (signer.hasFullMessage())
+ {
+ fail("signer indicates full message");
+ }
+ byte[] recoverableMessage = signer.getRecoveredMessage();
+
+ // sanity check, normally the nonce is ignored in eMRTD specs (PKI Technical Report)
+ if (!Arrays.areEqual(nonce, recoverableMessage))
+ {
+ fail("Nonce compare with recoverable part of message failed");
+ }
+ }
+ else
+ {
+ fail("recoverable + nonce failed.");
+ }
+ }
+
+ private static final byte[] longMessage = Base64.decode(
+ "VVNIKzErU0U2ODAxNTMyOTcxOSsyKzErNisyKzErMTo6OTk5OTk5OTk5OTk5"
+ + "OTo6OSsyOjo3Nzc3Nzc3Nzc3Nzc3Ojo5Kys1OjIwMTMwNDA1OjExMzUyMCdV"
+ + "U0ErMTo6OjE2OjEnVVNDKzRmYjk3YzFhNDI5ZGIyZDYnVVNBKzY6MTY6MTox"
+ + "MDoxKzE0OjIwNDgrMTI6/vn3S0h96eNhfmPN6OZUxXhd815h0tP871Hl+V1r"
+ + "fHHUXvrPXmjHV0vdb8fYY1zxwvnQUcFBWXT43PFi7Xbow0/9e9l6/mhs1UJq"
+ + "VPvp+ELbeXfn4Nj02ttk0e3H5Hfa69NYRuHv1WBO6lfizNnM9m9XYmh9TOrg"
+ + "f9rDRtd+ZNbf4lz9fPTt9OXyxOJWRPr/0FLzxUVsddplfHxM3ndETFD7ffjI"
+ + "/mhRYuL8WXZ733LeWFRCeOzKzmDz/HvT3GZx/XJMbFpqyOZjedzh6vZr1vrD"
+ + "615TQfN7wtJJ29bN2Hvzb2f1xGHaXl7af0/w9dpR2dr7/HzuZEJKYc7JSkv4"
+ + "/k37yERIbcrfbVTeVtR+dcVoeeRT41fmzMfzf8RnWOX4YMNifl0rMTM68EFA"
+ + "QSdCR00rMzgwKzk5OTk5OTk5J0RUTSsxMzc6MjAxMzA0MDU6MTAyJ0ZUWCtB"
+ + "QUkrKytJTlZPSUNFIFRFU1QnUkZGK09OOjEyMzQ1NidSRkYrRFE6MjIyMjIy"
+ + "MjIyJ0RUTSsxNzE6MjAxMzA0MDE6MTAyJ05BRCtTVSs5OTk5OTk5OTk5OTk5"
+ + "Ojo5KytURVNUIFNVUFBMSUVSOjpUcmFzZSByZWdpc3RlciBYWFhYWFhYK1Rl"
+ + "c3QgYWRkcmVzcyBzdXBwbGllcitDaXR5KysxMjM0NStERSdSRkYrVkE6QTEy"
+ + "MzQ1Njc4J05BRCtTQ08rOTk5OTk5OTk5OTk5OTo6OSsrVEVTVCBTVVBQTElF"
+ + "Ujo6VHJhc2UgcmVnaXN0ZXIgWFhYWFhYWCtUZXN0IGFkZHJlc3Mgc3VwcGxp"
+ + "ZXIrQ2l0eSsrMTIzNDUrREUnUkZGK1ZBOkExMjM0NTY3OCdOQUQrQlkrODg4"
+ + "ODg4ODg4ODg4ODo6OSdOQUQrSVYrNzc3Nzc3Nzc3Nzc3Nzo6OSsrVEVTVCBC"
+ + "VVlFUitUZXN0IGFkZHJlc3MgYnV5ZXIrQ2l0eTIrKzU0MzIxK0RFJ1JGRitW"
+ + "QTpKODc2NTQzMjEnTkFEK0JDTys3Nzc3Nzc3Nzc3Nzc3Ojo5KytURVNUIEJV"
+ + "WUVSK1Rlc3QgYWRkcmVzcyBidXllcitDaXR5MisrNTQzMjErREUnUkZGK1ZB"
+ + "Oko4NzY1NDMyMSdOQUQrRFArODg4ODg4ODg4ODg4ODo6OSdOQUQrUFIrNzc3"
+ + "Nzc3Nzc3Nzc3Nzo6OSdDVVgrMjpFVVI6NCdQQVQrMzUnRFRNKzEzOjIwMTMw"
+ + "NjI0OjEwMidMSU4rMSsrMTExMTExMTExMTExMTpFTidQSUErMStBQUFBQUFB"
+ + "OlNBJ0lNRCtGK00rOjo6UFJPRFVDVCBURVNUIDEnUVRZKzQ3OjEwLjAwMCdN"
+ + "T0ErNjY6Ny4wMCdQUkkrQUFCOjEuMDAnUFJJK0FBQTowLjcwJ1JGRitPTjox"
+ + "MjM0NTYnUkZGK0RROjIyMjIyMjIyMidUQVgrNytWQVQrKys6OjoyMS4wMDAn"
+ + "QUxDK0ErKysxK1REJ1BDRCsxOjMwLjAwMCdNT0ErMjA0OjMuMDAnTElOKzIr"
+ + "KzIyMjIyMjIyMjIyMjI6RU4nUElBKzErQkJCQkJCQjpTQSdJTUQrRitNKzo6"
+ + "OlBST0RVQ1QgVEVTVCAyJ1FUWSs0NzoyMC4wMDAnTU9BKzY2OjgwLjAwJ1BS"
+ + "SStBQUI6NS4wMCdQUkkrQUFBOjQuMDAnUkZGK09OOjEyMzQ1NidSRkYrRFE6"
+ + "MjIyMjIyMjIyJ1RBWCs3K1ZBVCsrKzo6OjIxLjAwMCdBTEMrQSsrKzErVEQn"
+ + "UENEKzE6MjAuMDAwJ01PQSsyMDQ6MjAuMDAnVU5TK1MnQ05UKzI6MidNT0Er"
+ + "Nzk6ODcuMDAnTU9BKzEzOToxMDUuMjcnTU9BKzEyNTo4Ny4wMCdNT0ErMjYw"
+ + "OjAuMDAnTU9BKzI1OTowLjAwJ01PQSsxNzY6MTguMjcnVEFYKzcrVkFUKysr"
+ + "Ojo6MjEuMDAwJ01PQSsxNzY6MTguMjcnTU9BKzEyNTo4Ny4wMCc=");
+
+ private static final byte[] shortPartialSig = Base64.decode(
+ "sb8yyKk6HM1cJhICScMx7QRQunRyrZ1fbI42+T+TBGNjOknvzKuvG7aftGX7"
+ + "O/RXuYgk6LTxpXv7+O5noUhMBsR2PKaHveuylU1WSPmDxDCui3kp4frqVH0w"
+ + "8Vjpl5CsKqBsmKkbGCKE+smM0xFXhYxV8QUTB2XsWNCQiFiHPgwbpfWzZUNY"
+ + "QPWd0A99P64EuUIYz1tkkDnLFmwQ19/PJu1a8orIQInmkVYWSsBsZ/7Ks6lx"
+ + "nDHpAvgiRe+OXmJ/yuQy1O3FJYdyoqvjYRPBu3qYeBK9+9L3lExLilImH5aD"
+ + "nJznaXcO8QFOxVPbrF2s4GdPIMDonEyAHdrnzoghlg==");
+
+ private void doShortPartialTest()
+ throws Exception
+ {
+ byte[] recovered = Hex.decode("5553482b312b534536383031353332393731392b322b312b362b322b312b313a3a393939393939393939393939393a3a392b323a3a373737373737373737373737373a3a392b2b353a32303133303430353a313133");
+ BigInteger exp = new BigInteger("10001", 16);
+ BigInteger mod = new BigInteger("b9b70b083da9e37e23cde8e654855db31e21d2d3fc11a5f91d2b3c311efa8f5e28c757dd6fc798631cb1b9d051c14119749cb122ad76e8c3fd7bd93abe282c026a14fba9f8023977a7a0d8b49a24d1ad87e4379a931846a1ef9520ea57e28c998cf65722683d0caaa0da8306973e2496a25cbd3cb4adb4b284e25604fabf12f385456c75da7c3c4cde37440cfb7db8c8fe6851e2bc59767b9f7218540238ac8acef3bc7bd3dc6671320c2c1a2ac8a6799ce1eaf62b9683ab1e1341b37b9249dbd6cd987b2f27b5c4619a1eda7f0fb0b59a519afbbc3cee640261cec90a4bb8fefbc844082dca9f549e56943e758579a453a357e6ccb37fc46718a5b8c3227e5d", 16);
+
+ AsymmetricKeyParameter pubKey = new RSAKeyParameters(false, mod, exp);
+
+ ISO9796d2PSSSigner pssSign = new ISO9796d2PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+
+ pssSign.init(false, pubKey);
+
+ pssSign.updateWithRecoveredMessage(shortPartialSig);
+
+ pssSign.update(longMessage, pssSign.getRecoveredMessage().length, longMessage.length - pssSign.getRecoveredMessage().length);
+
+ if (!pssSign.verifySignature(shortPartialSig))
+ {
+ fail("short partial PSS sig verification failed.");
+ }
+
+ byte[] mm = pssSign.getRecoveredMessage();
+
+ if (!Arrays.areEqual(recovered, mm))
+ {
+ fail("short partial PSS recovery failed");
+ }
+ }
+
+ private void doFullMessageTest()
+ throws Exception
+ {
+ BigInteger modulus = new BigInteger(1, Hex.decode("CDCBDABBF93BE8E8294E32B055256BBD0397735189BF75816341BB0D488D05D627991221DF7D59835C76A4BB4808ADEEB779E7794504E956ADC2A661B46904CDC71337DD29DDDD454124EF79CFDD7BC2C21952573CEFBA485CC38C6BD2428809B5A31A898A6B5648CAA4ED678D9743B589134B7187478996300EDBA16271A861"));
+ BigInteger pubExp = new BigInteger(1, Hex.decode("010001"));
+ BigInteger privExp = new BigInteger(1, Hex.decode("4BA6432AD42C74AA5AFCB6DF60FD57846CBC909489994ABD9C59FE439CC6D23D6DE2F3EA65B8335E796FD7904CA37C248367997257AFBD82B26F1A30525C447A236C65E6ADE43ECAAF7283584B2570FA07B340D9C9380D88EAACFFAEEFE7F472DBC9735C3FF3A3211E8A6BBFD94456B6A33C17A2C4EC18CE6335150548ED126D"));
+
+ RSAKeyParameters pubParams = new RSAKeyParameters(false, modulus, pubExp);
+ RSAKeyParameters privParams = new RSAKeyParameters(true, modulus, privExp);
+
+ AsymmetricBlockCipher rsaEngine = new RSABlindedEngine();
+
+ // set challenge to all zero's for verification
+ byte[] challenge = new byte[8];
+
+ ISO9796d2PSSSigner pssSign = new ISO9796d2PSSSigner(new RSAEngine(), new SHA256Digest(), 20, true);
+
+ pssSign.init(true, privParams);
+
+ pssSign.update(challenge, 0, challenge.length);
+
+ byte[] sig = pssSign.generateSignature();
+
+ pssSign.init(false, pubParams);
+
+ pssSign.updateWithRecoveredMessage(sig);
+
+ if (!pssSign.verifySignature(sig))
+ {
+ fail("challenge PSS sig verification failed.");
+ }
+
+ byte[] mm = pssSign.getRecoveredMessage();
+
+ if (!Arrays.areEqual(challenge, mm))
+ {
+ fail("challenge partial PSS recovery failed");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ doTest1();
+ doTest2();
+ doTest3();
+ doTest4();
+ doTest5();
+ doTest6();
+ doTest7();
+ doTest8();
+ doTest9();
+ doTest10();
+ doTest11();
+ doTest12();
+ doTest13();
+ doShortPartialTest();
+ doFullMessageTest();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ISO9796Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ISO9797Alg3MacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ISO9797Alg3MacTest.java
new file mode 100644
index 00000000..96b5fc46
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ISO9797Alg3MacTest.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.macs.ISO9797Alg3Mac;
+import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class ISO9797Alg3MacTest
+ extends SimpleTest
+{
+ static byte[] keyBytes = Hex.decode("7CA110454A1A6E570131D9619DC1376E");
+
+ static byte[] input1 = "Hello World !!!!".getBytes();
+
+ static byte[] output1 = Hex.decode("F09B856213BAB83B");
+
+ public ISO9797Alg3MacTest()
+ {
+ }
+
+ public void performTest()
+ {
+ KeyParameter key = new KeyParameter(keyBytes);
+ BlockCipher cipher = new DESEngine();
+ Mac mac = new ISO9797Alg3Mac(cipher);
+
+ //
+ // standard DAC - zero IV
+ //
+ mac.init(key);
+
+ mac.update(input1, 0, input1.length);
+
+ byte[] out = new byte[8];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output1))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output1)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // reset
+ //
+ mac.reset();
+
+ mac.init(key);
+
+ for (int i = 0; i != input1.length / 2; i++)
+ {
+ mac.update(input1[i]);
+ }
+
+ mac.update(input1, input1.length / 2, input1.length - (input1.length / 2));
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output1))
+ {
+ fail("Reset failed - expected " + new String(Hex.encode(output1)) + " got " + new String(Hex.encode(out)));
+ }
+
+ testMacWithIv();
+ }
+
+ private void testMacWithIv()
+ {
+ byte[] inputData = new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
+ byte[] key = new byte[]{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
+ byte[] zeroIv = new byte[8];
+ byte[] nonZeroIv = new byte[]{0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3, 0x4};
+
+ KeyParameter simpleParameter = new KeyParameter(key);
+ ParametersWithIV zeroIvParameter = new ParametersWithIV(new KeyParameter(key), zeroIv);
+
+ ISO9797Alg3Mac mac1 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
+
+ // we calculate a reference MAC with a null IV
+ mac1.init(simpleParameter);
+ mac1.update(inputData, 0, inputData.length);
+ byte[] output1 = new byte[mac1.getMacSize()];
+ mac1.doFinal(output1, 0);
+
+ // we then check that passing a vector of 0s is the same as not using any IV
+ ISO9797Alg3Mac mac2 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
+ mac2.init(zeroIvParameter);
+ mac2.update(inputData, 0, inputData.length);
+ byte[] output2 = new byte[mac2.getMacSize()];
+ mac2.doFinal(output2, 0);
+ if (!Arrays.areEqual(output1, output2))
+ {
+ fail("zero IV test failed");
+ }
+
+ // and then check that a non zero IV parameter produces a different results.
+ ParametersWithIV nonZeroIvParameter = new ParametersWithIV(new KeyParameter(key), nonZeroIv);
+ mac2 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
+ mac2.init(nonZeroIvParameter);
+ mac2.update(inputData, 0, inputData.length);
+ output2 = new byte[mac2.getMacSize()];
+ mac2.doFinal(output2, 0);
+ if (Arrays.areEqual(output1, output2))
+ {
+ fail("non-zero IV test failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "ISO9797Alg3Mac";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ISO9797Alg3MacTest());
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KDF1GeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDF1GeneratorTest.java
new file mode 100644
index 00000000..62f9997f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDF1GeneratorTest.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.digests.ShortenedDigest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.generators.KDF1BytesGenerator;
+import org.bouncycastle.crypto.params.ISO18033KDFParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * KDF1 tests - vectors from ISO 18033.
+ */
+public class KDF1GeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("d6e168c5f256a2dcff7ef12facd390f393c7a88d");
+ private byte[] mask1 = Hex.decode(
+ "0742ba966813af75536bb6149cc44fc256fd6406df79665bc31dc5"
+ + "a62f70535e52c53015b9d37d412ff3c1193439599e1b628774c50d9c"
+ + "cb78d82c425e4521ee47b8c36a4bcffe8b8112a89312fc04420a39de"
+ + "99223890e74ce10378bc515a212b97b8a6447ba6a8870278");
+
+ private byte[] seed2 = Hex.decode(
+ "032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d7643741"
+ + "52e0ac009e509e7ba30cd2f1778e113b64e135cf4e2292c75efe5288edfda4");
+ private byte[] mask2 = Hex.decode(
+ "5f8de105b5e96b2e490ddecbd147dd1def7e3b8e0e6a26eb7b956ccb8b3bdc1ca9"
+ + "75bc57c3989e8fbad31a224655d800c46954840ff32052cdf0d640562bdfadfa263c"
+ + "fccf3c52b29f2af4a1869959bc77f854cf15bd7a25192985a842dbff8e13efee5b7e"
+ + "7e55bbe4d389647c686a9a9ab3fb889b2d7767d3837eea4e0a2f04");
+
+ private byte[] seed3 = seed2;
+ private byte[] mask3= Hex.decode(
+ "09e2decf2a6e1666c2f6071ff4298305e2643fd510a2403db42a8743cb989de86e"
+ + "668d168cbe604611ac179f819a3d18412e9eb45668f2923c087c12fee0c5a0d2a8aa"
+ + "70185401fbbd99379ec76c663e875a60b4aacb1319fa11c3365a8b79a44669f26fb5"
+ + "55c80391847b05eca1cb5cf8c2d531448d33fbaca19f6410ee1fcb");
+
+
+ public KDF1GeneratorTest()
+ {
+ }
+
+ public void performTest()
+ {
+ checkMask(1, new KDF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed1, mask1);
+ checkMask(2, new KDF1BytesGenerator(new SHA1Digest()), seed2, mask2);
+ checkMask(3, new KDF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed3, mask3);
+
+ try
+ {
+ new KDF1BytesGenerator(new SHA1Digest()).generateBytes(new byte[10], 0, 20);
+
+ fail("short input array not caught");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ byte[] seed,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(new ISO18033KDFParameters(seed));
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("KDF1 failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "KDF1";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new KDF1GeneratorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KDF2GeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDF2GeneratorTest.java
new file mode 100644
index 00000000..84a435a6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDF2GeneratorTest.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.ShortenedDigest;
+import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
+import org.bouncycastle.crypto.params.KDFParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * KDF2 tests - vectors from ISO 18033.
+ */
+public class KDF2GeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("d6e168c5f256a2dcff7ef12facd390f393c7a88d");
+ private byte[] mask1 = Hex.decode(
+ "df79665bc31dc5a62f70535e52c53015b9d37d412ff3c119343959"
+ + "9e1b628774c50d9ccb78d82c425e4521ee47b8c36a4bcffe8b8112a8"
+ + "9312fc04420a39de99223890e74ce10378bc515a212b97b8a6447ba6"
+ + "a8870278f0262727ca041fa1aa9f7b5d1cf7f308232fe861");
+
+ private byte[] seed2 = Hex.decode(
+ "032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d7643741"
+ + "52e0ac009e509e7ba30cd2f1778e113b64e135cf4e2292c75efe5288edfda4");
+ private byte[] mask2 = Hex.decode(
+ "10a2403db42a8743cb989de86e668d168cbe604611ac179f819a3d18412e9eb456"
+ + "68f2923c087c12fee0c5a0d2a8aa70185401fbbd99379ec76c663e875a60b4aacb13"
+ + "19fa11c3365a8b79a44669f26fb555c80391847b05eca1cb5cf8c2d531448d33fbac"
+ + "a19f6410ee1fcb260892670e0814c348664f6a7248aaf998a3acc6");
+ private byte[] adjustedMask2 = Hex.decode(
+ "10a2403db42a8743cb989de86e668d168cbe6046e23ff26f741e87949a3bba1311ac1"
+ + "79f819a3d18412e9eb45668f2923c087c1299005f8d5fd42ca257bc93e8fee0c5a0d2"
+ + "a8aa70185401fbbd99379ec76c663e9a29d0b70f3fe261a59cdc24875a60b4aacb131"
+ + "9fa11c3365a8b79a44669f26fba933d012db213d7e3b16349");
+
+ private byte[] sha1Mask = Hex.decode(
+ "0e6a26eb7b956ccb8b3bdc1ca975bc57c3989e8fbad31a224655d800c46954840ff32"
+ + "052cdf0d640562bdfadfa263cfccf3c52b29f2af4a1869959bc77f854cf15bd7a2519"
+ + "2985a842dbff8e13efee5b7e7e55bbe4d389647c686a9a9ab3fb889b2d7767d3837ee"
+ + "a4e0a2f04b53ca8f50fb31225c1be2d0126c8c7a4753b0807");
+
+ private byte[] seed3 = Hex.decode("CA7C0F8C3FFA87A96E1B74AC8E6AF594347BB40A");
+ private byte[] mask3 = Hex.decode("744AB703F5BC082E59185F6D049D2D367DB245C2");
+
+ private byte[] seed4 = Hex.decode("0499B502FC8B5BAFB0F4047E731D1F9FD8CD0D8881");
+ private byte[] mask4 = Hex.decode("03C62280C894E103C680B13CD4B4AE740A5EF0C72547292F82DC6B1777F47D63BA9D1EA732DBF386");
+
+ public KDF2GeneratorTest()
+ {
+ }
+
+ public void performTest()
+ {
+ checkMask(1, new KDF2BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed1, mask1);
+ checkMask(2, new KDF2BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed2, mask2);
+ checkMask(3, new KDF2BytesGenerator(new SHA256Digest()), seed2, adjustedMask2);
+ checkMask(4, new KDF2BytesGenerator(new SHA1Digest()), seed2, sha1Mask);
+ checkMask(5, new KDF2BytesGenerator(new SHA1Digest()), seed3, mask3);
+ checkMask(6, new KDF2BytesGenerator(new SHA1Digest()), seed4, mask4);
+
+ try
+ {
+ new KDF2BytesGenerator(new SHA1Digest()).generateBytes(new byte[10], 0, 20);
+
+ fail("short input array not caught");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ byte[] seed,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(new KDFParameters(seed, new byte[0]));
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("KDF2 failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "KDF2";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new KDF2GeneratorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFCounterGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFCounterGeneratorTest.java
new file mode 100644
index 00000000..7078da26
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFCounterGeneratorTest.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.bouncycastle.crypto.test.cavp.CAVPReader;
+import org.bouncycastle.crypto.test.cavp.KDFCounterTests;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class KDFCounterGeneratorTest
+ extends SimpleTest
+{
+
+ private static void testCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFCTR_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark", e);
+ }
+ }
+
+ public String getName()
+ {
+ return this.getClass().getSimpleName();
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testCounter();
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new KDFCounterGeneratorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFDoublePipelineIteratorGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFDoublePipelineIteratorGeneratorTest.java
new file mode 100644
index 00000000..37aaf668
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFDoublePipelineIteratorGeneratorTest.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.bouncycastle.crypto.test.cavp.CAVPReader;
+import org.bouncycastle.crypto.test.cavp.KDFDoublePipelineCounterTests;
+import org.bouncycastle.crypto.test.cavp.KDFDoublePipelineIterationNoCounterTests;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class KDFDoublePipelineIteratorGeneratorTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return this.getClass().getSimpleName();
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testDoublePipelineIterationCounter();
+ testDoublePipelineIterationNoCounter();
+ }
+
+ private static void testDoublePipelineIterationCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFDoublePipelineCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFDblPipelineCounter_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFDoublePipelineIterationCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark", e);
+ }
+ }
+
+ private static void testDoublePipelineIterationNoCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFDoublePipelineIterationNoCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFDblPipelineNoCounter_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFDblPipelineIterationNoCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark", e);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new KDFDoublePipelineIteratorGeneratorTest());
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFFeedbackGeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFFeedbackGeneratorTest.java
new file mode 100644
index 00000000..0eba7068
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KDFFeedbackGeneratorTest.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.bouncycastle.crypto.test.cavp.CAVPReader;
+import org.bouncycastle.crypto.test.cavp.KDFFeedbackCounterTests;
+import org.bouncycastle.crypto.test.cavp.KDFFeedbackNoCounterTests;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class KDFFeedbackGeneratorTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return this.getClass().getSimpleName();
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testFeedbackCounter();
+ testFeedbackNoCounter();
+ }
+
+ private static void testFeedbackCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFFeedbackCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFFeedbackCounter_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFFeedbackCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark ", e);
+ }
+ }
+
+ private static void testFeedbackNoCounter()
+ {
+
+ CAVPReader cavpReader = new CAVPReader(new KDFFeedbackNoCounterTests());
+
+ final InputStream stream = CAVPReader.class.getResourceAsStream("KDFFeedbackNoCounter_gen.rsp");
+ final Reader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
+ cavpReader.setInput("KDFFeedbackNoCounter", reader);
+
+ try
+ {
+ cavpReader.readAll();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException("Something is rotten in the state of Denmark", e);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new KDFDoublePipelineIteratorGeneratorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/MD2DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/MD2DigestTest.java
new file mode 100644
index 00000000..443ec2e0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/MD2DigestTest.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.MD2Digest;
+
+/**
+ * standard vector test for MD2
+ * from RFC1319 by B.Kaliski of RSA Laboratories April 1992
+ *
+ */
+public class MD2DigestTest
+ extends DigestTest
+{
+ static final String messages[] =
+ {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ static final String digests[] =
+ {
+ "8350e5a3e24c153df2275c9f80692773",
+ "32ec01ec4a6dac72c0ab96fb34c0b5d1",
+ "da853b0d3f88d99b30283a69e6ded6bb",
+ "ab4f496bfb2a530b219ff33031fe06b0",
+ "4e8ddff3650292ab5a4108c3aa47940b",
+ "da33def2a42df13975352846c30338cd",
+ "d5976f79d83d3a0dc9806c3c66f3efd8"
+ };
+
+ MD2DigestTest()
+ {
+ super(new MD2Digest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(
+ Digest digest)
+ {
+ return new MD2Digest((MD2Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MD2DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/MD4DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/MD4DigestTest.java
new file mode 100644
index 00000000..d57a29ce
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/MD4DigestTest.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.MD4Digest;
+
+/**
+ * standard vector test for MD4 from RFC 1320.
+ */
+public class MD4DigestTest
+ extends DigestTest
+{
+ static private String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ static private String[] digests =
+ {
+ "31d6cfe0d16ae931b73c59d7e0c089c0",
+ "bde52cb31de33e46245e05fbdbd6fb24",
+ "a448017aaf21d8525fc10ae87aa6729d",
+ "e33b4ddc9c38f2199c3e7b164fcc0536"
+ };
+
+ MD4DigestTest()
+ {
+ super(new MD4Digest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new MD4Digest((MD4Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MD4DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/MD5DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/MD5DigestTest.java
new file mode 100644
index 00000000..ab9cab27
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/MD5DigestTest.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.MD5Digest;
+
+/**
+ * standard vector test for MD5 from "Handbook of Applied Cryptography", page 345.
+ */
+public class MD5DigestTest
+ extends DigestTest
+{
+ static final String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghijklmnopqrstuvwxyz"
+ };
+
+ static final String[] digests =
+ {
+ "d41d8cd98f00b204e9800998ecf8427e",
+ "0cc175b9c0f1b6a831c399e269772661",
+ "900150983cd24fb0d6963f7d28e17f72",
+ "c3fcd3d76192e4007dfb496cca67e13b"
+ };
+
+ MD5DigestTest()
+ {
+ super(new MD5Digest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new MD5Digest((MD5Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MD5DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/MD5HMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/MD5HMacTest.java
new file mode 100644
index 00000000..cfce1d91
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/MD5HMacTest.java
@@ -0,0 +1,98 @@
+
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.MD5Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * MD5 HMac Test, test vectors from RFC 2202
+ */
+public class MD5HMacTest
+ extends SimpleTest
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "9294727a3638bb1c13f48ef8158bfc9d",
+ "750c783e6ab0b503eaa86e310a5db738",
+ "56be34521d144c88dbb8c733f0e8b3f6",
+ "697eaf0aca3a3aea3a75164746ffaa79",
+ "56461ef2342edc00f9bab995690efd4c",
+ "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd",
+ "6f630fad67cda0ee1fb1f562db3aa53e"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+ };
+
+ public String getName()
+ {
+ return "MD5HMac";
+ }
+
+ public void performTest()
+ {
+ HMac hmac = new HMac(new MD5Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ fail("Vector " + i + " failed");
+ }
+ }
+
+ // test reset
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ fail("Reset with vector " + vector + " failed");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MD5HMacTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/MGF1GeneratorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/MGF1GeneratorTest.java
new file mode 100644
index 00000000..e7e99fe2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/MGF1GeneratorTest.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationFunction;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.digests.ShortenedDigest;
+import org.bouncycastle.crypto.generators.MGF1BytesGenerator;
+import org.bouncycastle.crypto.params.MGFParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * MGF1 tests - vectors from ISO 18033 for KDF1 (equivalent).
+ */
+public class MGF1GeneratorTest
+ extends SimpleTest
+{
+ private byte[] seed1 = Hex.decode("d6e168c5f256a2dcff7ef12facd390f393c7a88d");
+ private byte[] mask1 = Hex.decode(
+ "0742ba966813af75536bb6149cc44fc256fd6406df79665bc31dc5"
+ + "a62f70535e52c53015b9d37d412ff3c1193439599e1b628774c50d9c"
+ + "cb78d82c425e4521ee47b8c36a4bcffe8b8112a89312fc04420a39de"
+ + "99223890e74ce10378bc515a212b97b8a6447ba6a8870278");
+
+ private byte[] seed2 = Hex.decode(
+ "032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d7643741"
+ + "52e0ac009e509e7ba30cd2f1778e113b64e135cf4e2292c75efe5288edfda4");
+ private byte[] mask2 = Hex.decode(
+ "5f8de105b5e96b2e490ddecbd147dd1def7e3b8e0e6a26eb7b956ccb8b3bdc1ca9"
+ + "75bc57c3989e8fbad31a224655d800c46954840ff32052cdf0d640562bdfadfa263c"
+ + "fccf3c52b29f2af4a1869959bc77f854cf15bd7a25192985a842dbff8e13efee5b7e"
+ + "7e55bbe4d389647c686a9a9ab3fb889b2d7767d3837eea4e0a2f04");
+
+ private byte[] seed3 = seed2;
+ private byte[] mask3= Hex.decode(
+ "09e2decf2a6e1666c2f6071ff4298305e2643fd510a2403db42a8743cb989de86e"
+ + "668d168cbe604611ac179f819a3d18412e9eb45668f2923c087c12fee0c5a0d2a8aa"
+ + "70185401fbbd99379ec76c663e875a60b4aacb1319fa11c3365a8b79a44669f26fb5"
+ + "55c80391847b05eca1cb5cf8c2d531448d33fbaca19f6410ee1fcb");
+
+ public void performTest()
+ {
+ checkMask(1, new MGF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed1, mask1);
+ checkMask(2, new MGF1BytesGenerator(new SHA1Digest()), seed2, mask2);
+ checkMask(3, new MGF1BytesGenerator(new ShortenedDigest(new SHA256Digest(), 20)), seed3, mask3);
+
+ try
+ {
+ new MGF1BytesGenerator(new SHA1Digest()).generateBytes(new byte[10], 0, 20);
+
+ fail("short input array not caught");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ private void checkMask(
+ int count,
+ DerivationFunction kdf,
+ byte[] seed,
+ byte[] result)
+ {
+ byte[] data = new byte[result.length];
+
+ kdf.init(new MGFParameters(seed));
+
+ kdf.generateBytes(data, 0, data.length);
+
+ if (!areEqual(result, data))
+ {
+ fail("MGF1 failed generator test " + count);
+ }
+ }
+
+ public String getName()
+ {
+ return "MGF1";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MGF1GeneratorTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/MacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/MacTest.java
new file mode 100644
index 00000000..5b05a608
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/MacTest.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
+import org.bouncycastle.crypto.macs.CFBBlockCipherMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.paddings.PKCS7Padding;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * MAC tester - vectors from
+ * <a href=http://www.itl.nist.gov/fipspubs/fip81.htm>FIP 81</a> and
+ * <a href=http://www.itl.nist.gov/fipspubs/fip113.htm>FIP 113</a>.
+ */
+public class MacTest
+ extends SimpleTest
+{
+ static byte[] keyBytes = Hex.decode("0123456789abcdef");
+ static byte[] ivBytes = Hex.decode("1234567890abcdef");
+
+ static byte[] input1 = Hex.decode("37363534333231204e6f77206973207468652074696d6520666f7220");
+
+ static byte[] output1 = Hex.decode("f1d30f68");
+ static byte[] output2 = Hex.decode("58d2e77e");
+ static byte[] output3 = Hex.decode("cd647403");
+
+ //
+ // these aren't NIST vectors, just for regression testing.
+ //
+ static byte[] input2 = Hex.decode("3736353433323120");
+
+ static byte[] output4 = Hex.decode("3af549c9");
+ static byte[] output5 = Hex.decode("188fbdd5");
+ static byte[] output6 = Hex.decode("7045eecd");
+
+ public MacTest()
+ {
+ }
+
+ public void performTest()
+ {
+ KeyParameter key = new KeyParameter(keyBytes);
+ BlockCipher cipher = new DESEngine();
+ Mac mac = new CBCBlockCipherMac(cipher);
+
+ //
+ // standard DAC - zero IV
+ //
+ mac.init(key);
+
+ mac.update(input1, 0, input1.length);
+
+ byte[] out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output1))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output1)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // mac with IV.
+ //
+ ParametersWithIV param = new ParametersWithIV(key, ivBytes);
+
+ mac.init(param);
+
+ mac.update(input1, 0, input1.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output2))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output2)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // CFB mac with IV - 8 bit CFB mode
+ //
+ param = new ParametersWithIV(key, ivBytes);
+
+ mac = new CFBBlockCipherMac(cipher);
+
+ mac.init(param);
+
+ mac.update(input1, 0, input1.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output3))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output3)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // word aligned data - zero IV
+ //
+ mac.init(key);
+
+ mac.update(input2, 0, input2.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output4))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output4)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // word aligned data - zero IV - CBC padding
+ //
+ mac = new CBCBlockCipherMac(cipher, new PKCS7Padding());
+
+ mac.init(key);
+
+ mac.update(input2, 0, input2.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output5))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output5)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // non-word aligned data - zero IV - CBC padding
+ //
+ mac.reset();
+
+ mac.update(input1, 0, input1.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output6))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output6)) + " got " + new String(Hex.encode(out)));
+ }
+
+ //
+ // non-word aligned data - zero IV - CBC padding
+ //
+ mac.init(key);
+
+ mac.update(input1, 0, input1.length);
+
+ out = new byte[4];
+
+ mac.doFinal(out, 0);
+
+ if (!areEqual(out, output6))
+ {
+ fail("Failed - expected " + new String(Hex.encode(output6)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ public String getName()
+ {
+ return "Mac";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new MacTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ModeTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ModeTest.java
new file mode 100644
index 00000000..6619b652
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ModeTest.java
@@ -0,0 +1,115 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * CFB/OFB Mode test of IV padding.
+ */
+public class ModeTest
+ implements Test
+{
+ public ModeTest()
+ {
+ }
+
+ private boolean isEqualTo(
+ byte[] a,
+ byte[] b)
+ {
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public TestResult perform()
+ {
+ KeyParameter key = new KeyParameter(Hex.decode("0011223344556677"));
+ byte[] input = Hex.decode("4e6f7720");
+ byte[] out1 = new byte[4];
+ byte[] out2 = new byte[4];
+
+
+ BlockCipher ofb = new OFBBlockCipher(new DESEngine(), 32);
+
+ ofb.init(true, new ParametersWithIV(key, Hex.decode("1122334455667788")));
+
+ ofb.processBlock(input, 0, out1, 0);
+
+ ofb.init(false, new ParametersWithIV(key, Hex.decode("1122334455667788")));
+ ofb.processBlock(out1, 0, out2, 0);
+
+ if (!isEqualTo(out2, input))
+ {
+ return new SimpleTestResult(false, getName() + ": test 1 - in != out");
+ }
+
+ ofb.init(true, new ParametersWithIV(key, Hex.decode("11223344")));
+
+ ofb.processBlock(input, 0, out1, 0);
+
+ ofb.init(false, new ParametersWithIV(key, Hex.decode("0000000011223344")));
+ ofb.processBlock(out1, 0, out2, 0);
+
+ if (!isEqualTo(out2, input))
+ {
+ return new SimpleTestResult(false, getName() + ": test 2 - in != out");
+ }
+
+ BlockCipher cfb = new CFBBlockCipher(new DESEngine(), 32);
+
+ cfb.init(true, new ParametersWithIV(key, Hex.decode("1122334455667788")));
+
+ cfb.processBlock(input, 0, out1, 0);
+
+ cfb.init(false, new ParametersWithIV(key, Hex.decode("1122334455667788")));
+ cfb.processBlock(out1, 0, out2, 0);
+
+ if (!isEqualTo(out2, input))
+ {
+ return new SimpleTestResult(false, getName() + ": test 3 - in != out");
+ }
+
+ cfb.init(true, new ParametersWithIV(key, Hex.decode("11223344")));
+
+ cfb.processBlock(input, 0, out1, 0);
+
+ cfb.init(false, new ParametersWithIV(key, Hex.decode("0000000011223344")));
+ cfb.processBlock(out1, 0, out2, 0);
+
+ if (!isEqualTo(out2, input))
+ {
+ return new SimpleTestResult(false, getName() + ": test 4 - in != out");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public String getName()
+ {
+ return "ModeTest";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ ModeTest test = new ModeTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/NISTCTSTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/NISTCTSTest.java
new file mode 100644
index 00000000..8e3df1c8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/NISTCTSTest.java
@@ -0,0 +1,160 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.modes.NISTCTSBlockCipher;
+import org.bouncycastle.crypto.modes.SICBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * CTS tester
+ */
+public class NISTCTSTest
+ extends SimpleTest
+{
+ private static KeyParameter key = new KeyParameter(Hex.decode("000102030405060708090a0b0c0d0e0f"));
+ private static byte[] iv = Hex.decode("101112131415161718191a1b1c1d1e1f");
+
+ private static byte[] singleBlock = Hex.decode("4920616d206f6e6520626c6f636b2e2e");
+ private static byte[] singleOut = Hex.decode("8aad2098847a2d74ac87de22745d2537");
+
+ private static byte[] twoBlock = Hex.decode("4920616d206174206c656173742074776f20626c6f636b73206c6f6e672e2e2e");
+
+ private static byte[] cs1TwoBlockOut = Hex.decode("3f07fd5816c3b96349eb9f6a074909d67237eb8aa9a7467b8a388c61d0e8f35a");
+ private static byte[] cs2TwoBlockOut = Hex.decode("3f07fd5816c3b96349eb9f6a074909d67237eb8aa9a7467b8a388c61d0e8f35a");
+ private static byte[] cs3TwoBlockOut = Hex.decode("7237eb8aa9a7467b8a388c61d0e8f35a3f07fd5816c3b96349eb9f6a074909d6");
+
+ private static byte[] notQuiteTwo = Hex.decode("4920616d206e6f742071756974652074776f2e2e2e");
+
+ private static byte[] cs1NotQuiteTwoBlockOut = Hex.decode("22ecf2ac77f098097ca69b72e3a46e9ca21bb5ebbc");
+ private static byte[] cs2NotQuiteTwoBlockOut = Hex.decode("f098097ca69b72e3a46e9ca21bb5ebbc22ecf2ac77");
+ private static byte[] cs3NotQuiteTwoBlockOut = Hex.decode("f098097ca69b72e3a46e9ca21bb5ebbc22ecf2ac77");
+
+ static byte[] in1 = Hex.decode("4e6f7720697320746865207420");
+ static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa");
+ static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98");
+ static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994");
+ static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6");
+
+ private void testCTS(
+ int id,
+ int type,
+ BlockCipher cipher,
+ CipherParameters params,
+ byte[] input,
+ byte[] output)
+ throws Exception
+ {
+ byte[] out = new byte[input.length];
+ BufferedBlockCipher engine = new NISTCTSBlockCipher(type, cipher);
+
+ engine.init(true, params);
+
+ int len = engine.processBytes(input, 0, input.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(output, out))
+ {
+ fail(id + " failed encryption expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+
+ engine.init(false, params);
+
+ len = engine.processBytes(output, 0, output.length, out, 0);
+
+ engine.doFinal(out, len);
+
+ if (!areEqual(input, out))
+ {
+ fail(id + " failed decryption expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ private void testExceptions() throws InvalidCipherTextException
+ {
+ BufferedBlockCipher engine = new NISTCTSBlockCipher(NISTCTSBlockCipher.CS1, new AESEngine());
+ CipherParameters params = new KeyParameter(new byte[engine.getBlockSize()]);
+ engine.init(true, params);
+
+ byte[] out = new byte[engine.getOutputSize(engine.getBlockSize())];
+
+ engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ fail("Expected CTS encrypt error on < 1 block input");
+ } catch(DataLengthException e)
+ {
+ // Expected
+ }
+
+ engine.init(true, params);
+ engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ } catch(DataLengthException e)
+ {
+ fail("Unexpected CTS encrypt error on == 1 block input");
+ }
+
+ engine.init(false, params);
+ engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ fail("Expected CTS decrypt error on < 1 block input");
+ } catch(DataLengthException e)
+ {
+ // Expected
+ }
+
+ engine.init(false, params);
+ engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0);
+ try
+ {
+ engine.doFinal(out, 0);
+ } catch(DataLengthException e)
+ {
+ fail("Unexpected CTS decrypt error on == 1 block input");
+ }
+
+ }
+
+ public String getName()
+ {
+ return "CTS";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testCTS(1, NISTCTSBlockCipher.CS1, new AESEngine(), new ParametersWithIV(key, iv), singleBlock, singleOut);
+ testCTS(2, NISTCTSBlockCipher.CS2, new AESEngine(), new ParametersWithIV(key, iv), singleBlock, singleOut);
+ testCTS(3, NISTCTSBlockCipher.CS3, new AESEngine(), new ParametersWithIV(key, iv), singleBlock, singleOut);
+
+ testCTS(4, NISTCTSBlockCipher.CS1, new AESEngine(), new ParametersWithIV(key, iv), twoBlock, cs1TwoBlockOut);
+ testCTS(5, NISTCTSBlockCipher.CS2, new AESEngine(), new ParametersWithIV(key, iv), twoBlock, cs2TwoBlockOut);
+ testCTS(6, NISTCTSBlockCipher.CS3, new AESEngine(), new ParametersWithIV(key, iv), twoBlock, cs3TwoBlockOut);
+
+ testCTS(7, NISTCTSBlockCipher.CS1, new AESEngine(), new ParametersWithIV(key, iv), notQuiteTwo, cs1NotQuiteTwoBlockOut);
+ testCTS(8, NISTCTSBlockCipher.CS2, new AESEngine(), new ParametersWithIV(key, iv), notQuiteTwo, cs2NotQuiteTwoBlockOut);
+ testCTS(9, NISTCTSBlockCipher.CS3, new AESEngine(), new ParametersWithIV(key, iv), notQuiteTwo, cs3NotQuiteTwoBlockOut);
+
+ testExceptions();
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new NISTCTSTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/NaccacheSternTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/NaccacheSternTest.java
new file mode 100644
index 00000000..90558558
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/NaccacheSternTest.java
@@ -0,0 +1,354 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.NaccacheSternEngine;
+import org.bouncycastle.crypto.generators.NaccacheSternKeyPairGenerator;
+import org.bouncycastle.crypto.params.NaccacheSternKeyGenerationParameters;
+import org.bouncycastle.crypto.params.NaccacheSternKeyParameters;
+import org.bouncycastle.crypto.params.NaccacheSternPrivateKeyParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test case for NaccacheStern cipher. For details on this cipher, please see
+ *
+ * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf
+ *
+ * Performs the following tests:
+ * <ul>
+ * <li> Toy example from the NaccacheSternPaper </li>
+ * <li> 768 bit test with text "Now is the time for all good men." (ripped from RSA test) and
+ * the same test with the first byte replaced by 0xFF </li>
+ * <li> 1024 bit test analog to 768 bit test </li>
+ * </ul>
+ */
+public class NaccacheSternTest
+ extends SimpleTest
+{
+ static final boolean debug = false;
+
+ static final NaccacheSternEngine cryptEng = new NaccacheSternEngine();
+
+ static final NaccacheSternEngine decryptEng = new NaccacheSternEngine();
+
+ static
+ {
+ cryptEng.setDebug(debug);
+ decryptEng.setDebug(debug);
+ }
+
+ // Values from NaccacheStern paper
+ static final BigInteger a = BigInteger.valueOf(101);
+
+ static final BigInteger u1 = BigInteger.valueOf(3);
+
+ static final BigInteger u2 = BigInteger.valueOf(5);
+
+ static final BigInteger u3 = BigInteger.valueOf(7);
+
+ static final BigInteger b = BigInteger.valueOf(191);
+
+ static final BigInteger v1 = BigInteger.valueOf(11);
+
+ static final BigInteger v2 = BigInteger.valueOf(13);
+
+ static final BigInteger v3 = BigInteger.valueOf(17);
+
+ static final BigInteger ONE = BigInteger.valueOf(1);
+
+ static final BigInteger TWO = BigInteger.valueOf(2);
+
+ static final BigInteger sigma = u1.multiply(u2).multiply(u3).multiply(v1)
+ .multiply(v2).multiply(v3);
+
+ static final BigInteger p = TWO.multiply(a).multiply(u1).multiply(u2)
+ .multiply(u3).add(ONE);
+
+ static final BigInteger q = TWO.multiply(b).multiply(v1).multiply(v2)
+ .multiply(v3).add(ONE);
+
+ static final BigInteger n = p.multiply(q);
+
+ static final BigInteger phi_n = p.subtract(ONE).multiply(q.subtract(ONE));
+
+ static final BigInteger g = BigInteger.valueOf(131);
+
+ static final Vector smallPrimes = new Vector();
+
+ // static final BigInteger paperTest = BigInteger.valueOf(202);
+
+ static final String input = "4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ static final BigInteger paperTest = BigInteger.valueOf(202);
+
+ //
+ // to check that we handling byte extension by big number correctly.
+ //
+ static final String edgeInput = "ff6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ public String getName()
+ {
+ return "NaccacheStern";
+ }
+
+ public void performTest()
+ {
+ // Test with given key from NaccacheSternPaper (totally insecure)
+
+ // First the Parameters from the NaccacheStern Paper
+ // (see http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf )
+
+ smallPrimes.addElement(u1);
+ smallPrimes.addElement(u2);
+ smallPrimes.addElement(u3);
+ smallPrimes.addElement(v1);
+ smallPrimes.addElement(v2);
+ smallPrimes.addElement(v3);
+
+ NaccacheSternKeyParameters pubParameters = new NaccacheSternKeyParameters(false, g, n, sigma.bitLength());
+
+ NaccacheSternPrivateKeyParameters privParameters = new NaccacheSternPrivateKeyParameters(g, n, sigma.bitLength(), smallPrimes, phi_n);
+
+ AsymmetricCipherKeyPair pair = new AsymmetricCipherKeyPair(pubParameters, privParameters);
+
+ // Initialize Engines with KeyPair
+
+ if (debug)
+ {
+ System.out.println("initializing encryption engine");
+ }
+ cryptEng.init(true, pair.getPublic());
+
+ if (debug)
+ {
+ System.out.println("initializing decryption engine");
+ }
+ decryptEng.init(false, pair.getPrivate());
+
+ byte[] data = paperTest.toByteArray();
+
+ if (!new BigInteger(data).equals(new BigInteger(enDeCrypt(data))))
+ {
+ fail("failed NaccacheStern paper test");
+ }
+
+ //
+ // key generation test
+ //
+
+ //
+ // 768 Bit test
+ //
+
+ if (debug)
+ {
+ System.out.println();
+ System.out.println("768 Bit TEST");
+ }
+
+ // specify key generation parameters
+ NaccacheSternKeyGenerationParameters genParam
+ = new NaccacheSternKeyGenerationParameters(new SecureRandom(), 768, 8, 30, debug);
+
+ // Initialize Key generator and generate key pair
+ NaccacheSternKeyPairGenerator pGen = new NaccacheSternKeyPairGenerator();
+ pGen.init(genParam);
+
+ pair = pGen.generateKeyPair();
+
+ if (((NaccacheSternKeyParameters)pair.getPublic()).getModulus().bitLength() < 768)
+ {
+ System.out.println("FAILED: key size is <786 bit, exactly "
+ + ((NaccacheSternKeyParameters)pair.getPublic()).getModulus().bitLength() + " bit");
+ fail("failed key generation (768) length test");
+ }
+
+ // Initialize Engines with KeyPair
+
+ if (debug)
+ {
+ System.out.println("initializing " + genParam.getStrength() + " bit encryption engine");
+ }
+ cryptEng.init(true, pair.getPublic());
+
+ if (debug)
+ {
+ System.out.println("initializing " + genParam.getStrength() + " bit decryption engine");
+ }
+ decryptEng.init(false, pair.getPrivate());
+
+ // Basic data input
+ data = Hex.decode(input);
+
+ if (!new BigInteger(1, data).equals(new BigInteger(1, enDeCrypt(data))))
+ {
+ fail("failed encryption decryption (" + genParam.getStrength() + ") basic test");
+ }
+
+ // Data starting with FF byte (would be interpreted as negative
+ // BigInteger)
+
+ data = Hex.decode(edgeInput);
+
+ if (!new BigInteger(1, data).equals(new BigInteger(1, enDeCrypt(data))))
+ {
+ fail("failed encryption decryption (" + genParam.getStrength() + ") edgeInput test");
+ }
+
+ //
+ // 1024 Bit Test
+ //
+/*
+ if (debug)
+ {
+ System.out.println();
+ System.out.println("1024 Bit TEST");
+ }
+
+ // specify key generation parameters
+ genParam = new NaccacheSternKeyGenerationParameters(new SecureRandom(), 1024, 8, 40);
+
+ pGen.init(genParam);
+ pair = pGen.generateKeyPair();
+
+ if (((NaccacheSternKeyParameters)pair.getPublic()).getModulus().bitLength() < 1024)
+ {
+ if (debug)
+ {
+ System.out.println("FAILED: key size is <1024 bit, exactly "
+ + ((NaccacheSternKeyParameters)pair.getPublic()).getModulus().bitLength() + " bit");
+ }
+ fail("failed key generation (1024) length test");
+ }
+
+ // Initialize Engines with KeyPair
+
+ if (debug)
+ {
+ System.out.println("initializing " + genParam.getStrength() + " bit encryption engine");
+ }
+ cryptEng.init(true, pair.getPublic());
+
+ if (debug)
+ {
+ System.out.println("initializing " + genParam.getStrength() + " bit decryption engine");
+ }
+ decryptEng.init(false, pair.getPrivate());
+
+ if (debug)
+ {
+ System.out.println("Data is " + new BigInteger(1, data));
+ }
+
+ // Basic data input
+ data = Hex.decode(input);
+
+ if (!new BigInteger(1, data).equals(new BigInteger(1, enDeCrypt(data))))
+ {
+ fail("failed encryption decryption (" + genParam.getStrength() + ") basic test");
+ }
+
+ // Data starting with FF byte (would be interpreted as negative
+ // BigInteger)
+
+ data = Hex.decode(edgeInput);
+
+ if (!new BigInteger(1, data).equals(new BigInteger(1, enDeCrypt(data))))
+ {
+ fail("failed encryption decryption (" + genParam.getStrength() + ") edgeInput test");
+ }
+*/
+ // END OF TEST CASE
+
+ try
+ {
+ new NaccacheSternEngine().processBlock(new byte[]{ 1 }, 0, 1);
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("failed initialisation check");
+ }
+
+ if (debug)
+ {
+ System.out.println("All tests successful");
+ }
+ }
+
+ private byte[] enDeCrypt(byte[] input)
+ {
+
+ // create work array
+ byte[] data = new byte[input.length];
+ System.arraycopy(input, 0, data, 0, data.length);
+
+ // Perform encryption like in the paper from Naccache-Stern
+ if (debug)
+ {
+ System.out.println("encrypting data. Data representation\n"
+ // + "As String:.... " + new String(data) + "\n"
+ + "As BigInteger: " + new BigInteger(1, data));
+ System.out.println("data length is " + data.length);
+ }
+
+ try
+ {
+ data = cryptEng.processData(data);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (debug)
+ {
+ System.out.println("failed - exception " + e.toString() + "\n" + e.getMessage());
+ }
+ fail("failed - exception " + e.toString() + "\n" + e.getMessage());
+ }
+
+ if (debug)
+ {
+ System.out.println("enrypted data representation\n"
+ // + "As String:.... " + new String(data) + "\n"
+ + "As BigInteger: " + new BigInteger(1, data));
+ System.out.println("data length is " + data.length);
+ }
+
+ try
+ {
+ data = decryptEng.processData(data);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (debug)
+ {
+ System.out.println("failed - exception " + e.toString() + "\n" + e.getMessage());
+ }
+ fail("failed - exception " + e.toString() + "\n" + e.getMessage());
+ }
+
+ if (debug)
+ {
+ System.out.println("decrypted data representation\n"
+ // + "As String:.... " + new String(data) + "\n"
+ + "As BigInteger: " + new BigInteger(1, data));
+ System.out.println("data length is " + data.length);
+ }
+
+ return data;
+
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new NaccacheSternTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/NoekeonTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/NoekeonTest.java
new file mode 100644
index 00000000..ebebbd64
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/NoekeonTest.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.NoekeonEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Noekeon tester
+ */
+public class NoekeonTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new NoekeonEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000",
+ "b1656851699e29fa24b70148503d2dfc"),
+ new BlockCipherVectorTest(1, new NoekeonEngine(),
+ new KeyParameter(Hex.decode("ffffffffffffffffffffffffffffffff")),
+ "ffffffffffffffffffffffffffffffff",
+ "2a78421b87c7d0924f26113f1d1349b2"),
+ new BlockCipherVectorTest(2, new NoekeonEngine(),
+ new KeyParameter(Hex.decode("b1656851699e29fa24b70148503d2dfc")),
+ "2a78421b87c7d0924f26113f1d1349b2",
+ "e2f687e07b75660ffc372233bc47532c")
+ };
+
+ NoekeonTest()
+ {
+ super(tests, new NoekeonEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "Noekeon";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new NoekeonTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/NonMemoableDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/NonMemoableDigestTest.java
new file mode 100644
index 00000000..d332393b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/NonMemoableDigestTest.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.NonMemoableDigest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * SHA1 HMac Test, test vectors from RFC 2202
+ */
+public class NonMemoableDigestTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "b617318655057264e28bc0b6fb378c8ef146be00",
+ "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
+ "125d7342b9ac11cd91a39af48aa17b4f63f175d3",
+ "4c9007f4026250c6bc8414f9bf50c86c2d7235da",
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112",
+ "e8e99d0f45237d786d6bbaa7965c7808bbff1a91",
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112",
+ "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+ };
+
+ public String getName()
+ {
+ return "NonMemoableDigest";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new NonMemoableDigest(new SHA1Digest()));
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed");
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ NonMemoableDigestTest test = new NonMemoableDigestTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/NullTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/NullTest.java
new file mode 100644
index 00000000..ef8ff4b5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/NullTest.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.engines.NullEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class NullTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new NullEngine(),
+ new KeyParameter(Hex.decode("00")), "00", "00")
+ };
+
+ NullTest()
+ {
+ super(tests, new NullEngine(), new KeyParameter(new byte[2]));
+ }
+
+ public String getName()
+ {
+ return "Null";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ BlockCipher engine = new NullEngine();
+
+ engine.init(true, null);
+
+ byte[] buf = new byte[1];
+
+ engine.processBlock(buf, 0, buf, 0);
+
+ if (buf[0] != 0)
+ {
+ fail("NullCipher changed data!");
+ }
+
+ byte[] shortBuf = new byte[0];
+
+ try
+ {
+ engine.processBlock(shortBuf, 0, buf, 0);
+
+ fail("failed short input check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ engine.processBlock(buf, 0, shortBuf, 0);
+
+ fail("failed short output check");
+ }
+ catch (DataLengthException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new NullTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/OAEPTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/OAEPTest.java
new file mode 100644
index 00000000..9d6b0cba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/OAEPTest.java
@@ -0,0 +1,830 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.pkcs.RSAPublicKey;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.encodings.OAEPEncoding;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class OAEPTest
+ extends SimpleTest
+{
+ static byte[] pubKeyEnc1 =
+ {
+ (byte)0x30, (byte)0x5a, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86,
+ (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05,
+ (byte)0x00, (byte)0x03, (byte)0x49, (byte)0x00, (byte)0x30, (byte)0x46, (byte)0x02, (byte)0x41,
+ (byte)0x00, (byte)0xaa, (byte)0x36, (byte)0xab, (byte)0xce, (byte)0x88, (byte)0xac, (byte)0xfd,
+ (byte)0xff, (byte)0x55, (byte)0x52, (byte)0x3c, (byte)0x7f, (byte)0xc4, (byte)0x52, (byte)0x3f,
+ (byte)0x90, (byte)0xef, (byte)0xa0, (byte)0x0d, (byte)0xf3, (byte)0x77, (byte)0x4a, (byte)0x25,
+ (byte)0x9f, (byte)0x2e, (byte)0x62, (byte)0xb4, (byte)0xc5, (byte)0xd9, (byte)0x9c, (byte)0xb5,
+ (byte)0xad, (byte)0xb3, (byte)0x00, (byte)0xa0, (byte)0x28, (byte)0x5e, (byte)0x53, (byte)0x01,
+ (byte)0x93, (byte)0x0e, (byte)0x0c, (byte)0x70, (byte)0xfb, (byte)0x68, (byte)0x76, (byte)0x93,
+ (byte)0x9c, (byte)0xe6, (byte)0x16, (byte)0xce, (byte)0x62, (byte)0x4a, (byte)0x11, (byte)0xe0,
+ (byte)0x08, (byte)0x6d, (byte)0x34, (byte)0x1e, (byte)0xbc, (byte)0xac, (byte)0xa0, (byte)0xa1,
+ (byte)0xf5, (byte)0x02, (byte)0x01, (byte)0x11
+ };
+
+ static byte[] privKeyEnc1 =
+ {
+ (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x52, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+ (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+ (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x82,
+ (byte)0x01, (byte)0x3c, (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x38, (byte)0x02, (byte)0x01,
+ (byte)0x00, (byte)0x02, (byte)0x41, (byte)0x00, (byte)0xaa, (byte)0x36, (byte)0xab, (byte)0xce,
+ (byte)0x88, (byte)0xac, (byte)0xfd, (byte)0xff, (byte)0x55, (byte)0x52, (byte)0x3c, (byte)0x7f,
+ (byte)0xc4, (byte)0x52, (byte)0x3f, (byte)0x90, (byte)0xef, (byte)0xa0, (byte)0x0d, (byte)0xf3,
+ (byte)0x77, (byte)0x4a, (byte)0x25, (byte)0x9f, (byte)0x2e, (byte)0x62, (byte)0xb4, (byte)0xc5,
+ (byte)0xd9, (byte)0x9c, (byte)0xb5, (byte)0xad, (byte)0xb3, (byte)0x00, (byte)0xa0, (byte)0x28,
+ (byte)0x5e, (byte)0x53, (byte)0x01, (byte)0x93, (byte)0x0e, (byte)0x0c, (byte)0x70, (byte)0xfb,
+ (byte)0x68, (byte)0x76, (byte)0x93, (byte)0x9c, (byte)0xe6, (byte)0x16, (byte)0xce, (byte)0x62,
+ (byte)0x4a, (byte)0x11, (byte)0xe0, (byte)0x08, (byte)0x6d, (byte)0x34, (byte)0x1e, (byte)0xbc,
+ (byte)0xac, (byte)0xa0, (byte)0xa1, (byte)0xf5, (byte)0x02, (byte)0x01, (byte)0x11, (byte)0x02,
+ (byte)0x40, (byte)0x0a, (byte)0x03, (byte)0x37, (byte)0x48, (byte)0x62, (byte)0x64, (byte)0x87,
+ (byte)0x69, (byte)0x5f, (byte)0x5f, (byte)0x30, (byte)0xbc, (byte)0x38, (byte)0xb9, (byte)0x8b,
+ (byte)0x44, (byte)0xc2, (byte)0xcd, (byte)0x2d, (byte)0xff, (byte)0x43, (byte)0x40, (byte)0x98,
+ (byte)0xcd, (byte)0x20, (byte)0xd8, (byte)0xa1, (byte)0x38, (byte)0xd0, (byte)0x90, (byte)0xbf,
+ (byte)0x64, (byte)0x79, (byte)0x7c, (byte)0x3f, (byte)0xa7, (byte)0xa2, (byte)0xcd, (byte)0xcb,
+ (byte)0x3c, (byte)0xd1, (byte)0xe0, (byte)0xbd, (byte)0xba, (byte)0x26, (byte)0x54, (byte)0xb4,
+ (byte)0xf9, (byte)0xdf, (byte)0x8e, (byte)0x8a, (byte)0xe5, (byte)0x9d, (byte)0x73, (byte)0x3d,
+ (byte)0x9f, (byte)0x33, (byte)0xb3, (byte)0x01, (byte)0x62, (byte)0x4a, (byte)0xfd, (byte)0x1d,
+ (byte)0x51, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0xd8, (byte)0x40, (byte)0xb4, (byte)0x16,
+ (byte)0x66, (byte)0xb4, (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3, (byte)0xb4,
+ (byte)0x32, (byte)0x04, (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x33, (byte)0x52, (byte)0x52,
+ (byte)0x4d, (byte)0x04, (byte)0x16, (byte)0xa5, (byte)0xa4, (byte)0x41, (byte)0xe7, (byte)0x00,
+ (byte)0xaf, (byte)0x46, (byte)0x12, (byte)0x0d, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0xc9,
+ (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4, (byte)0x53, (byte)0xf6, (byte)0x34,
+ (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1, (byte)0xd9, (byte)0x35, (byte)0x3f,
+ (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66, (byte)0xb1, (byte)0xd0, (byte)0x5a,
+ (byte)0x0f, (byte)0x20, (byte)0x35, (byte)0x02, (byte)0x8b, (byte)0x9d, (byte)0x89, (byte)0x02,
+ (byte)0x20, (byte)0x59, (byte)0x0b, (byte)0x95, (byte)0x72, (byte)0xa2, (byte)0xc2, (byte)0xa9,
+ (byte)0xc4, (byte)0x06, (byte)0x05, (byte)0x9d, (byte)0xc2, (byte)0xab, (byte)0x2f, (byte)0x1d,
+ (byte)0xaf, (byte)0xeb, (byte)0x7e, (byte)0x8b, (byte)0x4f, (byte)0x10, (byte)0xa7, (byte)0x54,
+ (byte)0x9e, (byte)0x8e, (byte)0xed, (byte)0xf5, (byte)0xb4, (byte)0xfc, (byte)0xe0, (byte)0x9e,
+ (byte)0x05, (byte)0x02, (byte)0x21, (byte)0x00, (byte)0x8e, (byte)0x3c, (byte)0x05, (byte)0x21,
+ (byte)0xfe, (byte)0x15, (byte)0xe0, (byte)0xea, (byte)0x06, (byte)0xa3, (byte)0x6f, (byte)0xf0,
+ (byte)0xf1, (byte)0x0c, (byte)0x99, (byte)0x52, (byte)0xc3, (byte)0x5b, (byte)0x7a, (byte)0x75,
+ (byte)0x14, (byte)0xfd, (byte)0x32, (byte)0x38, (byte)0xb8, (byte)0x0a, (byte)0xad, (byte)0x52,
+ (byte)0x98, (byte)0x62, (byte)0x8d, (byte)0x51, (byte)0x02, (byte)0x20, (byte)0x36, (byte)0x3f,
+ (byte)0xf7, (byte)0x18, (byte)0x9d, (byte)0xa8, (byte)0xe9, (byte)0x0b, (byte)0x1d, (byte)0x34,
+ (byte)0x1f, (byte)0x71, (byte)0xd0, (byte)0x9b, (byte)0x76, (byte)0xa8, (byte)0xa9, (byte)0x43,
+ (byte)0xe1, (byte)0x1d, (byte)0x10, (byte)0xb2, (byte)0x4d, (byte)0x24, (byte)0x9f, (byte)0x2d,
+ (byte)0xea, (byte)0xfe, (byte)0xf8, (byte)0x0c, (byte)0x18, (byte)0x26
+ };
+
+ static byte[] output1 =
+ {
+ (byte)0x1b, (byte)0x8f, (byte)0x05, (byte)0xf9, (byte)0xca, (byte)0x1a, (byte)0x79, (byte)0x52,
+ (byte)0x6e, (byte)0x53, (byte)0xf3, (byte)0xcc, (byte)0x51, (byte)0x4f, (byte)0xdb, (byte)0x89,
+ (byte)0x2b, (byte)0xfb, (byte)0x91, (byte)0x93, (byte)0x23, (byte)0x1e, (byte)0x78, (byte)0xb9,
+ (byte)0x92, (byte)0xe6, (byte)0x8d, (byte)0x50, (byte)0xa4, (byte)0x80, (byte)0xcb, (byte)0x52,
+ (byte)0x33, (byte)0x89, (byte)0x5c, (byte)0x74, (byte)0x95, (byte)0x8d, (byte)0x5d, (byte)0x02,
+ (byte)0xab, (byte)0x8c, (byte)0x0f, (byte)0xd0, (byte)0x40, (byte)0xeb, (byte)0x58, (byte)0x44,
+ (byte)0xb0, (byte)0x05, (byte)0xc3, (byte)0x9e, (byte)0xd8, (byte)0x27, (byte)0x4a, (byte)0x9d,
+ (byte)0xbf, (byte)0xa8, (byte)0x06, (byte)0x71, (byte)0x40, (byte)0x94, (byte)0x39, (byte)0xd2
+ };
+
+ static byte[] pubKeyEnc2 =
+ {
+ (byte)0x30, (byte)0x4c, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86,
+ (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05,
+ (byte)0x00, (byte)0x03, (byte)0x3b, (byte)0x00, (byte)0x30, (byte)0x38, (byte)0x02, (byte)0x33,
+ (byte)0x00, (byte)0xa3, (byte)0x07, (byte)0x9a, (byte)0x90, (byte)0xdf, (byte)0x0d, (byte)0xfd,
+ (byte)0x72, (byte)0xac, (byte)0x09, (byte)0x0c, (byte)0xcc, (byte)0x2a, (byte)0x78, (byte)0xb8,
+ (byte)0x74, (byte)0x13, (byte)0x13, (byte)0x3e, (byte)0x40, (byte)0x75, (byte)0x9c, (byte)0x98,
+ (byte)0xfa, (byte)0xf8, (byte)0x20, (byte)0x4f, (byte)0x35, (byte)0x8a, (byte)0x0b, (byte)0x26,
+ (byte)0x3c, (byte)0x67, (byte)0x70, (byte)0xe7, (byte)0x83, (byte)0xa9, (byte)0x3b, (byte)0x69,
+ (byte)0x71, (byte)0xb7, (byte)0x37, (byte)0x79, (byte)0xd2, (byte)0x71, (byte)0x7b, (byte)0xe8,
+ (byte)0x34, (byte)0x77, (byte)0xcf, (byte)0x02, (byte)0x01, (byte)0x03
+ };
+
+ static byte[] privKeyEnc2 =
+ {
+ (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x13, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+ (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+ (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x81,
+ (byte)0xfe, (byte)0x30, (byte)0x81, (byte)0xfb, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02,
+ (byte)0x33, (byte)0x00, (byte)0xa3, (byte)0x07, (byte)0x9a, (byte)0x90, (byte)0xdf, (byte)0x0d,
+ (byte)0xfd, (byte)0x72, (byte)0xac, (byte)0x09, (byte)0x0c, (byte)0xcc, (byte)0x2a, (byte)0x78,
+ (byte)0xb8, (byte)0x74, (byte)0x13, (byte)0x13, (byte)0x3e, (byte)0x40, (byte)0x75, (byte)0x9c,
+ (byte)0x98, (byte)0xfa, (byte)0xf8, (byte)0x20, (byte)0x4f, (byte)0x35, (byte)0x8a, (byte)0x0b,
+ (byte)0x26, (byte)0x3c, (byte)0x67, (byte)0x70, (byte)0xe7, (byte)0x83, (byte)0xa9, (byte)0x3b,
+ (byte)0x69, (byte)0x71, (byte)0xb7, (byte)0x37, (byte)0x79, (byte)0xd2, (byte)0x71, (byte)0x7b,
+ (byte)0xe8, (byte)0x34, (byte)0x77, (byte)0xcf, (byte)0x02, (byte)0x01, (byte)0x03, (byte)0x02,
+ (byte)0x32, (byte)0x6c, (byte)0xaf, (byte)0xbc, (byte)0x60, (byte)0x94, (byte)0xb3, (byte)0xfe,
+ (byte)0x4c, (byte)0x72, (byte)0xb0, (byte)0xb3, (byte)0x32, (byte)0xc6, (byte)0xfb, (byte)0x25,
+ (byte)0xa2, (byte)0xb7, (byte)0x62, (byte)0x29, (byte)0x80, (byte)0x4e, (byte)0x68, (byte)0x65,
+ (byte)0xfc, (byte)0xa4, (byte)0x5a, (byte)0x74, (byte)0xdf, (byte)0x0f, (byte)0x8f, (byte)0xb8,
+ (byte)0x41, (byte)0x3b, (byte)0x52, (byte)0xc0, (byte)0xd0, (byte)0xe5, (byte)0x3d, (byte)0x9b,
+ (byte)0x59, (byte)0x0f, (byte)0xf1, (byte)0x9b, (byte)0xe7, (byte)0x9f, (byte)0x49, (byte)0xdd,
+ (byte)0x21, (byte)0xe5, (byte)0xeb, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0xcf, (byte)0x20,
+ (byte)0x35, (byte)0x02, (byte)0x8b, (byte)0x9d, (byte)0x86, (byte)0x98, (byte)0x40, (byte)0xb4,
+ (byte)0x16, (byte)0x66, (byte)0xb4, (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3,
+ (byte)0xb4, (byte)0x32, (byte)0x04, (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x91, (byte)0x02,
+ (byte)0x1a, (byte)0x00, (byte)0xc9, (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4,
+ (byte)0x53, (byte)0xf6, (byte)0x34, (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1,
+ (byte)0xd9, (byte)0x35, (byte)0x3f, (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66,
+ (byte)0xb1, (byte)0xd0, (byte)0x5f, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0x8a, (byte)0x15,
+ (byte)0x78, (byte)0xac, (byte)0x5d, (byte)0x13, (byte)0xaf, (byte)0x10, (byte)0x2b, (byte)0x22,
+ (byte)0xb9, (byte)0x99, (byte)0xcd, (byte)0x74, (byte)0x61, (byte)0xf1, (byte)0x5e, (byte)0x6d,
+ (byte)0x22, (byte)0xcc, (byte)0x03, (byte)0x23, (byte)0xdf, (byte)0xdf, (byte)0x0b, (byte)0x02,
+ (byte)0x1a, (byte)0x00, (byte)0x86, (byte)0x55, (byte)0x21, (byte)0x4a, (byte)0xc5, (byte)0x4d,
+ (byte)0x8d, (byte)0x4e, (byte)0xcd, (byte)0x61, (byte)0x77, (byte)0xf1, (byte)0xc7, (byte)0x36,
+ (byte)0x90, (byte)0xce, (byte)0x2a, (byte)0x48, (byte)0x2c, (byte)0x8b, (byte)0x05, (byte)0x99,
+ (byte)0xcb, (byte)0xe0, (byte)0x3f, (byte)0x02, (byte)0x1a, (byte)0x00, (byte)0x83, (byte)0xef,
+ (byte)0xef, (byte)0xb8, (byte)0xa9, (byte)0xa4, (byte)0x0d, (byte)0x1d, (byte)0xb6, (byte)0xed,
+ (byte)0x98, (byte)0xad, (byte)0x84, (byte)0xed, (byte)0x13, (byte)0x35, (byte)0xdc, (byte)0xc1,
+ (byte)0x08, (byte)0xf3, (byte)0x22, (byte)0xd0, (byte)0x57, (byte)0xcf, (byte)0x8d
+ };
+
+ static byte[] output2 =
+ {
+ (byte)0x14, (byte)0xbd, (byte)0xdd, (byte)0x28, (byte)0xc9, (byte)0x83, (byte)0x35, (byte)0x19,
+ (byte)0x23, (byte)0x80, (byte)0xe8, (byte)0xe5, (byte)0x49, (byte)0xb1, (byte)0x58, (byte)0x2a,
+ (byte)0x8b, (byte)0x40, (byte)0xb4, (byte)0x48, (byte)0x6d, (byte)0x03, (byte)0xa6, (byte)0xa5,
+ (byte)0x31, (byte)0x1f, (byte)0x1f, (byte)0xd5, (byte)0xf0, (byte)0xa1, (byte)0x80, (byte)0xe4,
+ (byte)0x17, (byte)0x53, (byte)0x03, (byte)0x29, (byte)0xa9, (byte)0x34, (byte)0x90, (byte)0x74,
+ (byte)0xb1, (byte)0x52, (byte)0x13, (byte)0x54, (byte)0x29, (byte)0x08, (byte)0x24, (byte)0x52,
+ (byte)0x62, (byte)0x51
+ };
+
+ static byte[] pubKeyEnc3 =
+ {
+ (byte)0x30, (byte)0x81, (byte)0x9d, (byte)0x30, (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a,
+ (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01,
+ (byte)0x05, (byte)0x00, (byte)0x03, (byte)0x81, (byte)0x8b, (byte)0x00, (byte)0x30, (byte)0x81,
+ (byte)0x87, (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xbb, (byte)0xf8, (byte)0x2f,
+ (byte)0x09, (byte)0x06, (byte)0x82, (byte)0xce, (byte)0x9c, (byte)0x23, (byte)0x38, (byte)0xac,
+ (byte)0x2b, (byte)0x9d, (byte)0xa8, (byte)0x71, (byte)0xf7, (byte)0x36, (byte)0x8d, (byte)0x07,
+ (byte)0xee, (byte)0xd4, (byte)0x10, (byte)0x43, (byte)0xa4, (byte)0x40, (byte)0xd6, (byte)0xb6,
+ (byte)0xf0, (byte)0x74, (byte)0x54, (byte)0xf5, (byte)0x1f, (byte)0xb8, (byte)0xdf, (byte)0xba,
+ (byte)0xaf, (byte)0x03, (byte)0x5c, (byte)0x02, (byte)0xab, (byte)0x61, (byte)0xea, (byte)0x48,
+ (byte)0xce, (byte)0xeb, (byte)0x6f, (byte)0xcd, (byte)0x48, (byte)0x76, (byte)0xed, (byte)0x52,
+ (byte)0x0d, (byte)0x60, (byte)0xe1, (byte)0xec, (byte)0x46, (byte)0x19, (byte)0x71, (byte)0x9d,
+ (byte)0x8a, (byte)0x5b, (byte)0x8b, (byte)0x80, (byte)0x7f, (byte)0xaf, (byte)0xb8, (byte)0xe0,
+ (byte)0xa3, (byte)0xdf, (byte)0xc7, (byte)0x37, (byte)0x72, (byte)0x3e, (byte)0xe6, (byte)0xb4,
+ (byte)0xb7, (byte)0xd9, (byte)0x3a, (byte)0x25, (byte)0x84, (byte)0xee, (byte)0x6a, (byte)0x64,
+ (byte)0x9d, (byte)0x06, (byte)0x09, (byte)0x53, (byte)0x74, (byte)0x88, (byte)0x34, (byte)0xb2,
+ (byte)0x45, (byte)0x45, (byte)0x98, (byte)0x39, (byte)0x4e, (byte)0xe0, (byte)0xaa, (byte)0xb1,
+ (byte)0x2d, (byte)0x7b, (byte)0x61, (byte)0xa5, (byte)0x1f, (byte)0x52, (byte)0x7a, (byte)0x9a,
+ (byte)0x41, (byte)0xf6, (byte)0xc1, (byte)0x68, (byte)0x7f, (byte)0xe2, (byte)0x53, (byte)0x72,
+ (byte)0x98, (byte)0xca, (byte)0x2a, (byte)0x8f, (byte)0x59, (byte)0x46, (byte)0xf8, (byte)0xe5,
+ (byte)0xfd, (byte)0x09, (byte)0x1d, (byte)0xbd, (byte)0xcb, (byte)0x02, (byte)0x01, (byte)0x11
+ };
+
+ static byte[] privKeyEnc3 =
+ {
+ (byte)0x30, (byte)0x82, (byte)0x02, (byte)0x75, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x30,
+ (byte)0x0d, (byte)0x06, (byte)0x09, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
+ (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x82,
+ (byte)0x02, (byte)0x5f, (byte)0x30, (byte)0x82, (byte)0x02, (byte)0x5b, (byte)0x02, (byte)0x01,
+ (byte)0x00, (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xbb, (byte)0xf8, (byte)0x2f,
+ (byte)0x09, (byte)0x06, (byte)0x82, (byte)0xce, (byte)0x9c, (byte)0x23, (byte)0x38, (byte)0xac,
+ (byte)0x2b, (byte)0x9d, (byte)0xa8, (byte)0x71, (byte)0xf7, (byte)0x36, (byte)0x8d, (byte)0x07,
+ (byte)0xee, (byte)0xd4, (byte)0x10, (byte)0x43, (byte)0xa4, (byte)0x40, (byte)0xd6, (byte)0xb6,
+ (byte)0xf0, (byte)0x74, (byte)0x54, (byte)0xf5, (byte)0x1f, (byte)0xb8, (byte)0xdf, (byte)0xba,
+ (byte)0xaf, (byte)0x03, (byte)0x5c, (byte)0x02, (byte)0xab, (byte)0x61, (byte)0xea, (byte)0x48,
+ (byte)0xce, (byte)0xeb, (byte)0x6f, (byte)0xcd, (byte)0x48, (byte)0x76, (byte)0xed, (byte)0x52,
+ (byte)0x0d, (byte)0x60, (byte)0xe1, (byte)0xec, (byte)0x46, (byte)0x19, (byte)0x71, (byte)0x9d,
+ (byte)0x8a, (byte)0x5b, (byte)0x8b, (byte)0x80, (byte)0x7f, (byte)0xaf, (byte)0xb8, (byte)0xe0,
+ (byte)0xa3, (byte)0xdf, (byte)0xc7, (byte)0x37, (byte)0x72, (byte)0x3e, (byte)0xe6, (byte)0xb4,
+ (byte)0xb7, (byte)0xd9, (byte)0x3a, (byte)0x25, (byte)0x84, (byte)0xee, (byte)0x6a, (byte)0x64,
+ (byte)0x9d, (byte)0x06, (byte)0x09, (byte)0x53, (byte)0x74, (byte)0x88, (byte)0x34, (byte)0xb2,
+ (byte)0x45, (byte)0x45, (byte)0x98, (byte)0x39, (byte)0x4e, (byte)0xe0, (byte)0xaa, (byte)0xb1,
+ (byte)0x2d, (byte)0x7b, (byte)0x61, (byte)0xa5, (byte)0x1f, (byte)0x52, (byte)0x7a, (byte)0x9a,
+ (byte)0x41, (byte)0xf6, (byte)0xc1, (byte)0x68, (byte)0x7f, (byte)0xe2, (byte)0x53, (byte)0x72,
+ (byte)0x98, (byte)0xca, (byte)0x2a, (byte)0x8f, (byte)0x59, (byte)0x46, (byte)0xf8, (byte)0xe5,
+ (byte)0xfd, (byte)0x09, (byte)0x1d, (byte)0xbd, (byte)0xcb, (byte)0x02, (byte)0x01, (byte)0x11,
+ (byte)0x02, (byte)0x81, (byte)0x81, (byte)0x00, (byte)0xa5, (byte)0xda, (byte)0xfc, (byte)0x53,
+ (byte)0x41, (byte)0xfa, (byte)0xf2, (byte)0x89, (byte)0xc4, (byte)0xb9, (byte)0x88, (byte)0xdb,
+ (byte)0x30, (byte)0xc1, (byte)0xcd, (byte)0xf8, (byte)0x3f, (byte)0x31, (byte)0x25, (byte)0x1e,
+ (byte)0x06, (byte)0x68, (byte)0xb4, (byte)0x27, (byte)0x84, (byte)0x81, (byte)0x38, (byte)0x01,
+ (byte)0x57, (byte)0x96, (byte)0x41, (byte)0xb2, (byte)0x94, (byte)0x10, (byte)0xb3, (byte)0xc7,
+ (byte)0x99, (byte)0x8d, (byte)0x6b, (byte)0xc4, (byte)0x65, (byte)0x74, (byte)0x5e, (byte)0x5c,
+ (byte)0x39, (byte)0x26, (byte)0x69, (byte)0xd6, (byte)0x87, (byte)0x0d, (byte)0xa2, (byte)0xc0,
+ (byte)0x82, (byte)0xa9, (byte)0x39, (byte)0xe3, (byte)0x7f, (byte)0xdc, (byte)0xb8, (byte)0x2e,
+ (byte)0xc9, (byte)0x3e, (byte)0xda, (byte)0xc9, (byte)0x7f, (byte)0xf3, (byte)0xad, (byte)0x59,
+ (byte)0x50, (byte)0xac, (byte)0xcf, (byte)0xbc, (byte)0x11, (byte)0x1c, (byte)0x76, (byte)0xf1,
+ (byte)0xa9, (byte)0x52, (byte)0x94, (byte)0x44, (byte)0xe5, (byte)0x6a, (byte)0xaf, (byte)0x68,
+ (byte)0xc5, (byte)0x6c, (byte)0x09, (byte)0x2c, (byte)0xd3, (byte)0x8d, (byte)0xc3, (byte)0xbe,
+ (byte)0xf5, (byte)0xd2, (byte)0x0a, (byte)0x93, (byte)0x99, (byte)0x26, (byte)0xed, (byte)0x4f,
+ (byte)0x74, (byte)0xa1, (byte)0x3e, (byte)0xdd, (byte)0xfb, (byte)0xe1, (byte)0xa1, (byte)0xce,
+ (byte)0xcc, (byte)0x48, (byte)0x94, (byte)0xaf, (byte)0x94, (byte)0x28, (byte)0xc2, (byte)0xb7,
+ (byte)0xb8, (byte)0x88, (byte)0x3f, (byte)0xe4, (byte)0x46, (byte)0x3a, (byte)0x4b, (byte)0xc8,
+ (byte)0x5b, (byte)0x1c, (byte)0xb3, (byte)0xc1, (byte)0x02, (byte)0x41, (byte)0x00, (byte)0xee,
+ (byte)0xcf, (byte)0xae, (byte)0x81, (byte)0xb1, (byte)0xb9, (byte)0xb3, (byte)0xc9, (byte)0x08,
+ (byte)0x81, (byte)0x0b, (byte)0x10, (byte)0xa1, (byte)0xb5, (byte)0x60, (byte)0x01, (byte)0x99,
+ (byte)0xeb, (byte)0x9f, (byte)0x44, (byte)0xae, (byte)0xf4, (byte)0xfd, (byte)0xa4, (byte)0x93,
+ (byte)0xb8, (byte)0x1a, (byte)0x9e, (byte)0x3d, (byte)0x84, (byte)0xf6, (byte)0x32, (byte)0x12,
+ (byte)0x4e, (byte)0xf0, (byte)0x23, (byte)0x6e, (byte)0x5d, (byte)0x1e, (byte)0x3b, (byte)0x7e,
+ (byte)0x28, (byte)0xfa, (byte)0xe7, (byte)0xaa, (byte)0x04, (byte)0x0a, (byte)0x2d, (byte)0x5b,
+ (byte)0x25, (byte)0x21, (byte)0x76, (byte)0x45, (byte)0x9d, (byte)0x1f, (byte)0x39, (byte)0x75,
+ (byte)0x41, (byte)0xba, (byte)0x2a, (byte)0x58, (byte)0xfb, (byte)0x65, (byte)0x99, (byte)0x02,
+ (byte)0x41, (byte)0x00, (byte)0xc9, (byte)0x7f, (byte)0xb1, (byte)0xf0, (byte)0x27, (byte)0xf4,
+ (byte)0x53, (byte)0xf6, (byte)0x34, (byte)0x12, (byte)0x33, (byte)0xea, (byte)0xaa, (byte)0xd1,
+ (byte)0xd9, (byte)0x35, (byte)0x3f, (byte)0x6c, (byte)0x42, (byte)0xd0, (byte)0x88, (byte)0x66,
+ (byte)0xb1, (byte)0xd0, (byte)0x5a, (byte)0x0f, (byte)0x20, (byte)0x35, (byte)0x02, (byte)0x8b,
+ (byte)0x9d, (byte)0x86, (byte)0x98, (byte)0x40, (byte)0xb4, (byte)0x16, (byte)0x66, (byte)0xb4,
+ (byte)0x2e, (byte)0x92, (byte)0xea, (byte)0x0d, (byte)0xa3, (byte)0xb4, (byte)0x32, (byte)0x04,
+ (byte)0xb5, (byte)0xcf, (byte)0xce, (byte)0x33, (byte)0x52, (byte)0x52, (byte)0x4d, (byte)0x04,
+ (byte)0x16, (byte)0xa5, (byte)0xa4, (byte)0x41, (byte)0xe7, (byte)0x00, (byte)0xaf, (byte)0x46,
+ (byte)0x15, (byte)0x03, (byte)0x02, (byte)0x40, (byte)0x54, (byte)0x49, (byte)0x4c, (byte)0xa6,
+ (byte)0x3e, (byte)0xba, (byte)0x03, (byte)0x37, (byte)0xe4, (byte)0xe2, (byte)0x40, (byte)0x23,
+ (byte)0xfc, (byte)0xd6, (byte)0x9a, (byte)0x5a, (byte)0xeb, (byte)0x07, (byte)0xdd, (byte)0xdc,
+ (byte)0x01, (byte)0x83, (byte)0xa4, (byte)0xd0, (byte)0xac, (byte)0x9b, (byte)0x54, (byte)0xb0,
+ (byte)0x51, (byte)0xf2, (byte)0xb1, (byte)0x3e, (byte)0xd9, (byte)0x49, (byte)0x09, (byte)0x75,
+ (byte)0xea, (byte)0xb7, (byte)0x74, (byte)0x14, (byte)0xff, (byte)0x59, (byte)0xc1, (byte)0xf7,
+ (byte)0x69, (byte)0x2e, (byte)0x9a, (byte)0x2e, (byte)0x20, (byte)0x2b, (byte)0x38, (byte)0xfc,
+ (byte)0x91, (byte)0x0a, (byte)0x47, (byte)0x41, (byte)0x74, (byte)0xad, (byte)0xc9, (byte)0x3c,
+ (byte)0x1f, (byte)0x67, (byte)0xc9, (byte)0x81, (byte)0x02, (byte)0x40, (byte)0x47, (byte)0x1e,
+ (byte)0x02, (byte)0x90, (byte)0xff, (byte)0x0a, (byte)0xf0, (byte)0x75, (byte)0x03, (byte)0x51,
+ (byte)0xb7, (byte)0xf8, (byte)0x78, (byte)0x86, (byte)0x4c, (byte)0xa9, (byte)0x61, (byte)0xad,
+ (byte)0xbd, (byte)0x3a, (byte)0x8a, (byte)0x7e, (byte)0x99, (byte)0x1c, (byte)0x5c, (byte)0x05,
+ (byte)0x56, (byte)0xa9, (byte)0x4c, (byte)0x31, (byte)0x46, (byte)0xa7, (byte)0xf9, (byte)0x80,
+ (byte)0x3f, (byte)0x8f, (byte)0x6f, (byte)0x8a, (byte)0xe3, (byte)0x42, (byte)0xe9, (byte)0x31,
+ (byte)0xfd, (byte)0x8a, (byte)0xe4, (byte)0x7a, (byte)0x22, (byte)0x0d, (byte)0x1b, (byte)0x99,
+ (byte)0xa4, (byte)0x95, (byte)0x84, (byte)0x98, (byte)0x07, (byte)0xfe, (byte)0x39, (byte)0xf9,
+ (byte)0x24, (byte)0x5a, (byte)0x98, (byte)0x36, (byte)0xda, (byte)0x3d, (byte)0x02, (byte)0x41,
+ (byte)0x00, (byte)0xb0, (byte)0x6c, (byte)0x4f, (byte)0xda, (byte)0xbb, (byte)0x63, (byte)0x01,
+ (byte)0x19, (byte)0x8d, (byte)0x26, (byte)0x5b, (byte)0xdb, (byte)0xae, (byte)0x94, (byte)0x23,
+ (byte)0xb3, (byte)0x80, (byte)0xf2, (byte)0x71, (byte)0xf7, (byte)0x34, (byte)0x53, (byte)0x88,
+ (byte)0x50, (byte)0x93, (byte)0x07, (byte)0x7f, (byte)0xcd, (byte)0x39, (byte)0xe2, (byte)0x11,
+ (byte)0x9f, (byte)0xc9, (byte)0x86, (byte)0x32, (byte)0x15, (byte)0x4f, (byte)0x58, (byte)0x83,
+ (byte)0xb1, (byte)0x67, (byte)0xa9, (byte)0x67, (byte)0xbf, (byte)0x40, (byte)0x2b, (byte)0x4e,
+ (byte)0x9e, (byte)0x2e, (byte)0x0f, (byte)0x96, (byte)0x56, (byte)0xe6, (byte)0x98, (byte)0xea,
+ (byte)0x36, (byte)0x66, (byte)0xed, (byte)0xfb, (byte)0x25, (byte)0x79, (byte)0x80, (byte)0x39,
+ (byte)0xf7
+ };
+
+ static byte[] output3 = Hex.decode(
+ "b8246b56a6ed5881aeb585d9a25b2ad790c417e080681bf1ac2bc3deb69d8bce"
+ + "f0c4366fec400af052a72e9b0effb5b3f2f192dbeaca03c12740057113bf1f06"
+ + "69ac22e9f3a7852e3c15d913cab0b8863a95c99294ce8674214954610346f4d4"
+ + "74b26f7c48b42ee68e1f572a1fc4026ac456b4f59f7b621ea1b9d88f64202fb1");
+
+ byte[] seed = {
+ (byte)0xaa, (byte)0xfd, (byte)0x12, (byte)0xf6, (byte)0x59,
+ (byte)0xca, (byte)0xe6, (byte)0x34, (byte)0x89, (byte)0xb4,
+ (byte)0x79, (byte)0xe5, (byte)0x07, (byte)0x6d, (byte)0xde,
+ (byte)0xc2, (byte)0xf0, (byte)0x6c, (byte)0xb5, (byte)0x8f
+ };
+
+ private class VecRand extends SecureRandom
+ {
+ byte[] seed;
+
+ VecRand(byte[] seed)
+ {
+ this.seed = seed;
+ }
+
+ public void nextBytes(
+ byte[] bytes)
+ {
+ System.arraycopy(seed, 0, bytes, 0, bytes.length);
+ }
+ }
+
+ private void baseOaepTest(
+ int id,
+ byte[] pubKeyEnc,
+ byte[] privKeyEnc,
+ byte[] output)
+ throws Exception
+ {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(pubKeyEnc);
+ ASN1InputStream dIn = new ASN1InputStream(bIn);
+
+ //
+ // extract the public key info.
+ //
+ RSAPublicKey pubStruct;
+
+ pubStruct = RSAPublicKey.getInstance(new SubjectPublicKeyInfo((ASN1Sequence)dIn.readObject()).parsePublicKey());
+
+
+ bIn = new ByteArrayInputStream(privKeyEnc);
+ dIn = new ASN1InputStream(bIn);
+
+ //
+ // extract the private key info.
+ //
+ RSAPrivateKey privStruct;
+
+ privStruct = RSAPrivateKey.getInstance(new PrivateKeyInfo((ASN1Sequence)dIn.readObject()).parsePrivateKey());
+
+ RSAKeyParameters pubParameters = new RSAKeyParameters(
+ false,
+ pubStruct.getModulus(),
+ pubStruct.getPublicExponent());
+
+ RSAKeyParameters privParameters = new RSAPrivateCrtKeyParameters(
+ privStruct.getModulus(),
+ privStruct.getPublicExponent(),
+ privStruct.getPrivateExponent(),
+ privStruct.getPrime1(),
+ privStruct.getPrime2(),
+ privStruct.getExponent1(),
+ privStruct.getExponent2(),
+ privStruct.getCoefficient());
+
+ byte[] input = new byte[]
+ { (byte)0x54, (byte)0x85, (byte)0x9b, (byte)0x34, (byte)0x2c, (byte)0x49, (byte)0xea, (byte)0x2a };
+
+ encDec("id(" + id + ")", pubParameters, privParameters, seed, input, output);
+
+ }
+
+ private void encDec(
+ String label,
+ RSAKeyParameters pubParameters,
+ RSAKeyParameters privParameters,
+ byte[] seed,
+ byte[] input,
+ byte[] output)
+ throws InvalidCipherTextException
+ {
+ AsymmetricBlockCipher cipher = new OAEPEncoding(new RSAEngine());
+
+ cipher.init(true, new ParametersWithRandom(pubParameters, new VecRand(seed)));
+
+ byte[] out;
+
+ out = cipher.processBlock(input, 0, input.length);
+
+ for (int i = 0; i != output.length; i++)
+ {
+ if (out[i] != output[i])
+ {
+ fail(label + " failed encryption");
+ }
+ }
+
+ cipher.init(false, privParameters);
+
+ out = cipher.processBlock(output, 0, output.length);
+
+ for (int i = 0; i != input.length; i++)
+ {
+ if (out[i] != input[i])
+ {
+ fail(label + " failed decoding");
+ }
+ }
+ }
+
+ /*
+ * RSA vector tests from PKCS#1 page
+ */
+ byte[] modulus_1024 = Hex.decode(
+ "a8b3b284af8eb50b387034a860f146c4"
+ + "919f318763cd6c5598c8ae4811a1e0ab"
+ + "c4c7e0b082d693a5e7fced675cf46685"
+ + "12772c0cbc64a742c6c630f533c8cc72"
+ + "f62ae833c40bf25842e984bb78bdbf97"
+ + "c0107d55bdb662f5c4e0fab9845cb514"
+ + "8ef7392dd3aaff93ae1e6b667bb3d424"
+ + "7616d4f5ba10d4cfd226de88d39f16fb");
+
+ byte[] pubExp_1024 = Hex.decode(
+ "010001");
+
+ byte[] privExp_1024 = Hex.decode(
+ "53339cfdb79fc8466a655c7316aca85c"
+ + "55fd8f6dd898fdaf119517ef4f52e8fd"
+ + "8e258df93fee180fa0e4ab29693cd83b"
+ + "152a553d4ac4d1812b8b9fa5af0e7f55"
+ + "fe7304df41570926f3311f15c4d65a73"
+ + "2c483116ee3d3d2d0af3549ad9bf7cbf"
+ + "b78ad884f84d5beb04724dc7369b31de"
+ + "f37d0cf539e9cfcdd3de653729ead5d1");
+
+ byte[] prime1_1024 = Hex.decode(
+ "d32737e7267ffe1341b2d5c0d150a81b"
+ + "586fb3132bed2f8d5262864a9cb9f30a"
+ + "f38be448598d413a172efb802c21acf1"
+ + "c11c520c2f26a471dcad212eac7ca39d");
+
+ byte[] prime2_1024 = Hex.decode(
+ "cc8853d1d54da630fac004f471f281c7"
+ + "b8982d8224a490edbeb33d3e3d5cc93c"
+ + "4765703d1dd791642f1f116a0dd852be"
+ + "2419b2af72bfe9a030e860b0288b5d77");
+
+ byte[] primeExp1_1024 = Hex.decode(
+ "0e12bf1718e9cef5599ba1c3882fe804"
+ + "6a90874eefce8f2ccc20e4f2741fb0a3"
+ + "3a3848aec9c9305fbecbd2d76819967d"
+ + "4671acc6431e4037968db37878e695c1");
+
+ byte[] primeExp2_1024 = Hex.decode(
+ "95297b0f95a2fa67d00707d609dfd4fc"
+ + "05c89dafc2ef6d6ea55bec771ea33373"
+ + "4d9251e79082ecda866efef13c459e1a"
+ + "631386b7e354c899f5f112ca85d71583");
+
+ byte[] crtCoef_1024 = Hex.decode(
+ "4f456c502493bdc0ed2ab756a3a6ed4d"
+ + "67352a697d4216e93212b127a63d5411"
+ + "ce6fa98d5dbefd73263e372814274381"
+ + "8166ed7dd63687dd2a8ca1d2f4fbd8e1");
+
+ byte[] input_1024_1 = Hex.decode(
+ "6628194e12073db03ba94cda9ef95323"
+ + "97d50dba79b987004afefe34");
+
+ byte[] seed_1024_1 = Hex.decode(
+ "18b776ea21069d69776a33e96bad48e1"
+ + "dda0a5ef");
+
+ byte[] output_1024_1 = Hex.decode(
+ "354fe67b4a126d5d35fe36c777791a3f"
+ + "7ba13def484e2d3908aff722fad468fb"
+ + "21696de95d0be911c2d3174f8afcc201"
+ + "035f7b6d8e69402de5451618c21a535f"
+ + "a9d7bfc5b8dd9fc243f8cf927db31322"
+ + "d6e881eaa91a996170e657a05a266426"
+ + "d98c88003f8477c1227094a0d9fa1e8c"
+ + "4024309ce1ecccb5210035d47ac72e8a");
+
+ byte[] input_1024_2 = Hex.decode(
+ "750c4047f547e8e41411856523298ac9"
+ + "bae245efaf1397fbe56f9dd5");
+
+ byte[] seed_1024_2 = Hex.decode(
+ "0cc742ce4a9b7f32f951bcb251efd925"
+ + "fe4fe35f");
+
+ byte[] output_1024_2 = Hex.decode(
+ "640db1acc58e0568fe5407e5f9b701df"
+ + "f8c3c91e716c536fc7fcec6cb5b71c11"
+ + "65988d4a279e1577d730fc7a29932e3f"
+ + "00c81515236d8d8e31017a7a09df4352"
+ + "d904cdeb79aa583adcc31ea698a4c052"
+ + "83daba9089be5491f67c1a4ee48dc74b"
+ + "bbe6643aef846679b4cb395a352d5ed1"
+ + "15912df696ffe0702932946d71492b44");
+
+ byte[] input_1024_3 = Hex.decode(
+ "d94ae0832e6445ce42331cb06d531a82"
+ + "b1db4baad30f746dc916df24d4e3c245"
+ + "1fff59a6423eb0e1d02d4fe646cf699d"
+ + "fd818c6e97b051");
+
+ byte[] seed_1024_3 = Hex.decode(
+ "2514df4695755a67b288eaf4905c36ee"
+ + "c66fd2fd");
+
+ byte[] output_1024_3 = Hex.decode(
+ "423736ed035f6026af276c35c0b3741b"
+ + "365e5f76ca091b4e8c29e2f0befee603"
+ + "595aa8322d602d2e625e95eb81b2f1c9"
+ + "724e822eca76db8618cf09c5343503a4"
+ + "360835b5903bc637e3879fb05e0ef326"
+ + "85d5aec5067cd7cc96fe4b2670b6eac3"
+ + "066b1fcf5686b68589aafb7d629b02d8"
+ + "f8625ca3833624d4800fb081b1cf94eb");
+
+ byte[] input_1024_4 = Hex.decode(
+ "52e650d98e7f2a048b4f86852153b97e"
+ + "01dd316f346a19f67a85");
+
+ byte[] seed_1024_4 = Hex.decode(
+ "c4435a3e1a18a68b6820436290a37cef"
+ + "b85db3fb");
+
+ byte[] output_1024_4 = Hex.decode(
+ "45ead4ca551e662c9800f1aca8283b05"
+ + "25e6abae30be4b4aba762fa40fd3d38e"
+ + "22abefc69794f6ebbbc05ddbb1121624"
+ + "7d2f412fd0fba87c6e3acd888813646f"
+ + "d0e48e785204f9c3f73d6d8239562722"
+ + "dddd8771fec48b83a31ee6f592c4cfd4"
+ + "bc88174f3b13a112aae3b9f7b80e0fc6"
+ + "f7255ba880dc7d8021e22ad6a85f0755");
+
+ byte[] input_1024_5 = Hex.decode(
+ "8da89fd9e5f974a29feffb462b49180f"
+ + "6cf9e802");
+
+ byte[] seed_1024_5 = Hex.decode(
+ "b318c42df3be0f83fea823f5a7b47ed5"
+ + "e425a3b5");
+
+ byte[] output_1024_5 = Hex.decode(
+ "36f6e34d94a8d34daacba33a2139d00a"
+ + "d85a9345a86051e73071620056b920e2"
+ + "19005855a213a0f23897cdcd731b4525"
+ + "7c777fe908202befdd0b58386b1244ea"
+ + "0cf539a05d5d10329da44e13030fd760"
+ + "dcd644cfef2094d1910d3f433e1c7c6d"
+ + "d18bc1f2df7f643d662fb9dd37ead905"
+ + "9190f4fa66ca39e869c4eb449cbdc439");
+
+ byte[] input_1024_6 = Hex.decode(
+ "26521050844271");
+
+ byte[] seed_1024_6 = Hex.decode(
+ "e4ec0982c2336f3a677f6a356174eb0c"
+ + "e887abc2");
+
+ byte[] output_1024_6 = Hex.decode(
+ "42cee2617b1ecea4db3f4829386fbd61"
+ + "dafbf038e180d837c96366df24c097b4"
+ + "ab0fac6bdf590d821c9f10642e681ad0"
+ + "5b8d78b378c0f46ce2fad63f74e0ad3d"
+ + "f06b075d7eb5f5636f8d403b9059ca76"
+ + "1b5c62bb52aa45002ea70baace08ded2"
+ + "43b9d8cbd62a68ade265832b56564e43"
+ + "a6fa42ed199a099769742df1539e8255");
+
+ byte[] modulus_1027 = Hex.decode(
+ "051240b6cc0004fa48d0134671c078c7"
+ + "c8dec3b3e2f25bc2564467339db38853"
+ + "d06b85eea5b2de353bff42ac2e46bc97"
+ + "fae6ac9618da9537a5c8f553c1e35762"
+ + "5991d6108dcd7885fb3a25413f53efca"
+ + "d948cb35cd9b9ae9c1c67626d113d57d"
+ + "de4c5bea76bb5bb7de96c00d07372e96"
+ + "85a6d75cf9d239fa148d70931b5f3fb0"
+ + "39");
+
+ byte[] pubExp_1027 = Hex.decode(
+ "010001");
+
+ byte[] privExp_1027 = Hex.decode(
+ "0411ffca3b7ca5e9e9be7fe38a85105e"
+ + "353896db05c5796aecd2a725161eb365"
+ + "1c8629a9b862b904d7b0c7b37f8cb5a1"
+ + "c2b54001018a00a1eb2cafe4ee4e9492"
+ + "c348bc2bedab4b9ebbf064e8eff322b9"
+ + "009f8eec653905f40df88a3cdc49d456"
+ + "7f75627d41aca624129b46a0b7c698e5"
+ + "e65f2b7ba102c749a10135b6540d0401");
+
+ byte[] prime1_1027 = Hex.decode(
+ "027458c19ec1636919e736c9af25d609"
+ + "a51b8f561d19c6bf6943dd1ee1ab8a4a"
+ + "3f232100bd40b88decc6ba235548b6ef"
+ + "792a11c9de823d0a7922c7095b6eba57"
+ + "01");
+
+ byte[] prime2_1027 = Hex.decode(
+ "0210ee9b33ab61716e27d251bd465f4b"
+ + "35a1a232e2da00901c294bf22350ce49"
+ + "0d099f642b5375612db63ba1f2038649"
+ + "2bf04d34b3c22bceb909d13441b53b51"
+ + "39");
+
+ byte[] primeExp1_1027 = Hex.decode(
+ "39fa028b826e88c1121b750a8b242fa9"
+ + "a35c5b66bdfd1fa637d3cc48a84a4f45"
+ + "7a194e7727e49f7bcc6e5a5a412657fc"
+ + "470c7322ebc37416ef458c307a8c0901");
+
+ byte[] primeExp2_1027 = Hex.decode(
+ "015d99a84195943979fa9e1be2c3c1b6"
+ + "9f432f46fd03e47d5befbbbfd6b1d137"
+ + "1d83efb330a3e020942b2fed115e5d02"
+ + "be24fd92c9019d1cecd6dd4cf1e54cc8"
+ + "99");
+
+ byte[] crtCoef_1027 = Hex.decode(
+ "01f0b7015170b3f5e42223ba30301c41"
+ + "a6d87cbb70e30cb7d3c67d25473db1f6"
+ + "cbf03e3f9126e3e97968279a865b2c2b"
+ + "426524cfc52a683d31ed30eb984be412"
+ + "ba");
+
+ byte[] input_1027_1 = Hex.decode(
+ "4a86609534ee434a6cbca3f7e962e76d"
+ + "455e3264c19f605f6e5ff6137c65c56d"
+ + "7fb344cd52bc93374f3d166c9f0c6f9c"
+ + "506bad19330972d2");
+
+ byte[] seed_1027_1 = Hex.decode(
+ "1cac19ce993def55f98203f6852896c9"
+ + "5ccca1f3");
+
+ byte[] output_1027_1 = Hex.decode(
+ "04cce19614845e094152a3fe18e54e33"
+ + "30c44e5efbc64ae16886cb1869014cc5"
+ + "781b1f8f9e045384d0112a135ca0d12e"
+ + "9c88a8e4063416deaae3844f60d6e96f"
+ + "e155145f4525b9a34431ca3766180f70"
+ + "e15a5e5d8e8b1a516ff870609f13f896"
+ + "935ced188279a58ed13d07114277d75c"
+ + "6568607e0ab092fd803a223e4a8ee0b1"
+ + "a8");
+
+ byte[] input_1027_2 = Hex.decode(
+ "b0adc4f3fe11da59ce992773d9059943"
+ + "c03046497ee9d9f9a06df1166db46d98"
+ + "f58d27ec074c02eee6cbe2449c8b9fc5"
+ + "080c5c3f4433092512ec46aa793743c8");
+
+ byte[] seed_1027_2 = Hex.decode(
+ "f545d5897585e3db71aa0cb8da76c51d"
+ + "032ae963");
+
+ byte[] output_1027_2 = Hex.decode(
+ "0097b698c6165645b303486fbf5a2a44"
+ + "79c0ee85889b541a6f0b858d6b6597b1"
+ + "3b854eb4f839af03399a80d79bda6578"
+ + "c841f90d645715b280d37143992dd186"
+ + "c80b949b775cae97370e4ec97443136c"
+ + "6da484e970ffdb1323a20847821d3b18"
+ + "381de13bb49aaea66530c4a4b8271f3e"
+ + "ae172cd366e07e6636f1019d2a28aed1"
+ + "5e");
+
+ byte[] input_1027_3 = Hex.decode(
+ "bf6d42e701707b1d0206b0c8b45a1c72"
+ + "641ff12889219a82bdea965b5e79a96b"
+ + "0d0163ed9d578ec9ada20f2fbcf1ea3c"
+ + "4089d83419ba81b0c60f3606da99");
+
+ byte[] seed_1027_3 = Hex.decode(
+ "ad997feef730d6ea7be60d0dc52e72ea"
+ + "cbfdd275");
+
+ byte[] output_1027_3 = Hex.decode(
+ "0301f935e9c47abcb48acbbe09895d9f"
+ + "5971af14839da4ff95417ee453d1fd77"
+ + "319072bb7297e1b55d7561cd9d1bb24c"
+ + "1a9a37c619864308242804879d86ebd0"
+ + "01dce5183975e1506989b70e5a834341"
+ + "54d5cbfd6a24787e60eb0c658d2ac193"
+ + "302d1192c6e622d4a12ad4b53923bca2"
+ + "46df31c6395e37702c6a78ae081fb9d0"
+ + "65");
+
+ byte[] input_1027_4 = Hex.decode(
+ "fb2ef112f5e766eb94019297934794f7"
+ + "be2f6fc1c58e");
+
+ byte[] seed_1027_4 = Hex.decode(
+ "136454df5730f73c807a7e40d8c1a312"
+ + "ac5b9dd3");
+
+ byte[] output_1027_4 = Hex.decode(
+ "02d110ad30afb727beb691dd0cf17d0a"
+ + "f1a1e7fa0cc040ec1a4ba26a42c59d0a"
+ + "796a2e22c8f357ccc98b6519aceb682e"
+ + "945e62cb734614a529407cd452bee3e4"
+ + "4fece8423cc19e55548b8b994b849c7e"
+ + "cde4933e76037e1d0ce44275b08710c6"
+ + "8e430130b929730ed77e09b015642c55"
+ + "93f04e4ffb9410798102a8e96ffdfe11"
+ + "e4");
+
+ byte[] input_1027_5 = Hex.decode(
+ "28ccd447bb9e85166dabb9e5b7d1adad"
+ + "c4b9d39f204e96d5e440ce9ad928bc1c"
+ + "2284");
+
+ byte[] seed_1027_5 = Hex.decode(
+ "bca8057f824b2ea257f2861407eef63d"
+ + "33208681");
+
+ byte[] output_1027_5 = Hex.decode(
+ "00dbb8a7439d90efd919a377c54fae8f"
+ + "e11ec58c3b858362e23ad1b8a4431079"
+ + "9066b99347aa525691d2adc58d9b06e3"
+ + "4f288c170390c5f0e11c0aa3645959f1"
+ + "8ee79e8f2be8d7ac5c23d061f18dd74b"
+ + "8c5f2a58fcb5eb0c54f99f01a8324756"
+ + "8292536583340948d7a8c97c4acd1e98"
+ + "d1e29dc320e97a260532a8aa7a758a1e"
+ + "c2");
+
+ byte[] input_1027_6 = Hex.decode(
+ "f22242751ec6b1");
+
+ byte[] seed_1027_6 = Hex.decode(
+ "2e7e1e17f647b5ddd033e15472f90f68"
+ + "12f3ac4e");
+
+ byte[] output_1027_6 = Hex.decode(
+ "00a5ffa4768c8bbecaee2db77e8f2eec"
+ + "99595933545520835e5ba7db9493d3e1"
+ + "7cddefe6a5f567624471908db4e2d83a"
+ + "0fbee60608fc84049503b2234a07dc83"
+ + "b27b22847ad8920ff42f674ef79b7628"
+ + "0b00233d2b51b8cb2703a9d42bfbc825"
+ + "0c96ec32c051e57f1b4ba528db89c37e"
+ + "4c54e27e6e64ac69635ae887d9541619"
+ + "a9");
+
+ private void oaepVecTest(
+ int keySize,
+ int no,
+ RSAKeyParameters pubParam,
+ RSAKeyParameters privParam,
+ byte[] seed,
+ byte[] input,
+ byte[] output)
+ throws Exception
+ {
+ encDec(keySize + " " + no, pubParam, privParam, seed, input, output);
+ }
+
+ public OAEPTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "OAEP";
+ }
+
+ public void performTest() throws Exception
+ {
+ baseOaepTest(1, pubKeyEnc1, privKeyEnc1, output1);
+ baseOaepTest(2, pubKeyEnc2, privKeyEnc2, output2);
+ baseOaepTest(3, pubKeyEnc3, privKeyEnc3, output3);
+
+ RSAKeyParameters pubParam = new RSAKeyParameters(false, new BigInteger(1, modulus_1024), new BigInteger(1, pubExp_1024));
+ RSAKeyParameters privParam = new RSAPrivateCrtKeyParameters(pubParam.getModulus(), pubParam.getExponent(), new BigInteger(1, privExp_1024), new BigInteger(1, prime1_1024), new BigInteger(1, prime2_1024), new BigInteger(1, primeExp1_1024), new BigInteger(1, primeExp2_1024), new BigInteger(1, crtCoef_1024));
+
+ oaepVecTest(1024, 1, pubParam, privParam, seed_1024_1, input_1024_1, output_1024_1);
+ oaepVecTest(1024, 2, pubParam, privParam, seed_1024_2, input_1024_2, output_1024_2);
+ oaepVecTest(1024, 3, pubParam, privParam, seed_1024_3, input_1024_3, output_1024_3);
+ oaepVecTest(1024, 4, pubParam, privParam, seed_1024_4, input_1024_4, output_1024_4);
+ oaepVecTest(1024, 5, pubParam, privParam, seed_1024_5, input_1024_5, output_1024_5);
+ oaepVecTest(1024, 6, pubParam, privParam, seed_1024_6, input_1024_6, output_1024_6);
+
+ pubParam = new RSAKeyParameters(false, new BigInteger(1, modulus_1027), new BigInteger(1, pubExp_1027));
+ privParam = new RSAPrivateCrtKeyParameters(pubParam.getModulus(), pubParam.getExponent(), new BigInteger(1, privExp_1027), new BigInteger(1, prime1_1027), new BigInteger(1, prime2_1027), new BigInteger(1, primeExp1_1027), new BigInteger(1, primeExp2_1027), new BigInteger(1, crtCoef_1027));
+
+ oaepVecTest(1027, 1, pubParam, privParam, seed_1027_1, input_1027_1, output_1027_1);
+ oaepVecTest(1027, 2, pubParam, privParam, seed_1027_2, input_1027_2, output_1027_2);
+ oaepVecTest(1027, 3, pubParam, privParam, seed_1027_3, input_1027_3, output_1027_3);
+ oaepVecTest(1027, 4, pubParam, privParam, seed_1027_4, input_1027_4, output_1027_4);
+ oaepVecTest(1027, 5, pubParam, privParam, seed_1027_5, input_1027_5, output_1027_5);
+ oaepVecTest(1027, 6, pubParam, privParam, seed_1027_6, input_1027_6, output_1027_6);
+
+ //
+ // OAEP - public encrypt, private decrypt differring hashes
+ //
+ AsymmetricBlockCipher cipher = new OAEPEncoding(new RSAEngine(), new SHA256Digest(), new SHA1Digest(), new byte[10]);
+
+ cipher.init(true, new ParametersWithRandom(pubParam, new SecureRandom()));
+
+ byte[] input = new byte[10];
+
+ byte[] out = cipher.processBlock(input, 0, input.length);
+
+ cipher.init(false, privParam);
+
+ out = cipher.processBlock(out, 0, out.length);
+
+ for (int i = 0; i != input.length; i++)
+ {
+ if (out[i] != input[i])
+ {
+ fail("mixed digest failed decoding");
+ }
+ }
+
+ cipher = new OAEPEncoding(new RSAEngine(), new SHA1Digest(), new SHA256Digest(), new byte[10]);
+
+ cipher.init(true, new ParametersWithRandom(pubParam, new SecureRandom()));
+
+ out = cipher.processBlock(input, 0, input.length);
+
+ cipher.init(false, privParam);
+
+ out = cipher.processBlock(out, 0, out.length);
+
+ for (int i = 0; i != input.length; i++)
+ {
+ if (out[i] != input[i])
+ {
+ fail("mixed digest failed decoding");
+ }
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new OAEPTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/OCBTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/OCBTest.java
new file mode 100644
index 00000000..26d7d124
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/OCBTest.java
@@ -0,0 +1,520 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.OCBBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Times;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from <a href="http://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB
+ * Authenticated-Encryption Algorithm</a>
+ */
+public class OCBTest
+ extends SimpleTest
+{
+ private static final String KEY_128 = "000102030405060708090A0B0C0D0E0F";
+ private static final String KEY_96 = "0F0E0D0C0B0A09080706050403020100";
+
+ /*
+ * Test vectors from Appendix A of the specification, containing the strings N, A, P, C in order
+ */
+
+ private static final String[][] TEST_VECTORS_128 = new String[][]{
+ { "BBAA99887766554433221100",
+ "",
+ "",
+ "785407BFFFC8AD9EDCC5520AC9111EE6" },
+ { "BBAA99887766554433221101",
+ "0001020304050607",
+ "0001020304050607",
+ "6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009" },
+ { "BBAA99887766554433221102",
+ "0001020304050607",
+ "",
+ "81017F8203F081277152FADE694A0A00" },
+ { "BBAA99887766554433221103",
+ "",
+ "0001020304050607",
+ "45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9" },
+ { "BBAA99887766554433221104",
+ "000102030405060708090A0B0C0D0E0F",
+ "000102030405060708090A0B0C0D0E0F",
+ "571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5701C1CCEC8FC3358" },
+ { "BBAA99887766554433221105",
+ "000102030405060708090A0B0C0D0E0F",
+ "",
+ "8CF761B6902EF764462AD86498CA6B97" },
+ { "BBAA99887766554433221106",
+ "",
+ "000102030405060708090A0B0C0D0E0F",
+ "5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436BDF06D8FA1ECA343D" },
+ { "BBAA99887766554433221107",
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "1CA2207308C87C010756104D8840CE1952F09673A448A122C92C62241051F57356D7F3C90BB0E07F" },
+ { "BBAA99887766554433221108",
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "",
+ "6DC225A071FC1B9F7C69F93B0F1E10DE" },
+ { "BBAA99887766554433221109",
+ "",
+ "000102030405060708090A0B0C0D0E0F1011121314151617",
+ "221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3CE725F32494B9F914D85C0B1EB38357FF" },
+ { "BBAA9988776655443322110A",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
+ "BD6F6C496201C69296C11EFD138A467ABD3C707924B964DEAFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240" },
+ { "BBAA9988776655443322110B",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
+ "",
+ "FE80690BEE8A485D11F32965BC9D2A32" },
+ { "BBAA9988776655443322110C",
+ "",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
+ "2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF46040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF" },
+ { "BBAA9988776655443322110D",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
+ "D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483A7035490C5769E60" },
+ { "BBAA9988776655443322110E",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
+ "",
+ "C5CD9D1850C141E358649994EE701B68" },
+ { "BBAA9988776655443322110F",
+ "",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
+ "4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95A98CA5F3000B1479" },
+ };
+
+ private static final String[][] TEST_VECTORS_96 = new String[][]{
+ { "BBAA9988776655443322110D",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
+ "1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FDAC4F02AA" },
+ };
+
+ public String getName()
+ {
+ return "OCB";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ byte[] K128 = Hex.decode(KEY_128);
+ for (int i = 0; i < TEST_VECTORS_128.length; ++i)
+ {
+ runTestCase("Test Case " + i, TEST_VECTORS_128[i], 128, K128);
+ }
+
+ byte[] K96 = Hex.decode(KEY_96);
+ for (int i = 0; i < TEST_VECTORS_96.length; ++i)
+ {
+ runTestCase("Test Case " + i, TEST_VECTORS_96[i], 96, K96);
+ }
+
+ runLongerTestCase(128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2");
+ runLongerTestCase(192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17");
+ runLongerTestCase(256, 128, "D90EB8E9C977C88B79DD793D7FFA161C");
+ runLongerTestCase(128, 96, "77A3D8E73589158D25D01209");
+ runLongerTestCase(192, 96, "05D56EAD2752C86BE6932C5E");
+ runLongerTestCase(256, 96, "5458359AC23B0CBA9E6330DD");
+ runLongerTestCase(128, 64, "192C9B7BD90BA06A");
+ runLongerTestCase(192, 64, "0066BC6E0EF34E24");
+ runLongerTestCase(256, 64, "7D4EA5D445501CBE");
+
+ randomTests();
+ outputSizeTests();
+ testExceptions();
+ }
+
+ private void testExceptions() throws InvalidCipherTextException
+ {
+ AEADBlockCipher ocb = createOCBCipher();
+
+ try
+ {
+ ocb = new OCBBlockCipher(new DESEngine(), new DESEngine());
+
+ fail("incorrect block size not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ocb.init(false, new KeyParameter(new byte[16]));
+
+ fail("illegal argument not picked up");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ AEADTestUtil.testReset(this, createOCBCipher(), createOCBCipher(), new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[15]));
+ AEADTestUtil.testTampering(this, ocb, new AEADParameters(new KeyParameter(new byte[16]), 128, new byte[15]));
+ AEADTestUtil.testOutputSizes(this, createOCBCipher(), new AEADParameters(new KeyParameter(new byte[16]), 128,
+ new byte[15]));
+ AEADTestUtil.testBufferSizeChecks(this, createOCBCipher(), new AEADParameters(new KeyParameter(new byte[16]),
+ 128, new byte[15]));
+ }
+
+ private void runTestCase(String testName, String[] testVector, int macLengthBits, byte[] K)
+ throws InvalidCipherTextException
+ {
+ int pos = 0;
+ byte[] N = Hex.decode(testVector[pos++]);
+ byte[] A = Hex.decode(testVector[pos++]);
+ byte[] P = Hex.decode(testVector[pos++]);
+ byte[] C = Hex.decode(testVector[pos++]);
+
+ int macLengthBytes = macLengthBits / 8;
+
+ KeyParameter keyParameter = new KeyParameter(K);
+ AEADParameters parameters = new AEADParameters(keyParameter, macLengthBits, N, A);
+
+ AEADBlockCipher encCipher = initOCBCipher(true, parameters);
+ AEADBlockCipher decCipher = initOCBCipher(false, parameters);
+
+ checkTestCase(encCipher, decCipher, testName, macLengthBytes, P, C);
+ checkTestCase(encCipher, decCipher, testName + " (reused)", macLengthBytes, P, C);
+
+ // Key reuse
+ AEADParameters keyReuseParams = AEADTestUtil.reuseKey(parameters);
+ encCipher.init(true, keyReuseParams);
+ decCipher.init(false, keyReuseParams);
+ checkTestCase(encCipher, decCipher, testName + " (key reuse)", macLengthBytes, P, C);
+ }
+
+ private BlockCipher createUnderlyingCipher()
+ {
+ return new AESEngine();
+ }
+
+ private AEADBlockCipher createOCBCipher()
+ {
+ return new OCBBlockCipher(createUnderlyingCipher(), createUnderlyingCipher());
+ }
+
+ private AEADBlockCipher initOCBCipher(boolean forEncryption, AEADParameters parameters)
+ {
+ AEADBlockCipher c = createOCBCipher();
+ c.init(forEncryption, parameters);
+ return c;
+ }
+
+ private void checkTestCase(AEADBlockCipher encCipher, AEADBlockCipher decCipher, String testName,
+ int macLengthBytes, byte[] P, byte[] C)
+ throws InvalidCipherTextException
+ {
+ byte[] tag = Arrays.copyOfRange(C, C.length - macLengthBytes, C.length);
+
+ {
+ byte[] enc = new byte[encCipher.getOutputSize(P.length)];
+ int len = encCipher.processBytes(P, 0, P.length, enc, 0);
+ len += encCipher.doFinal(enc, len);
+
+ if (enc.length != len)
+ {
+ fail("encryption reported incorrect length: " + testName);
+ }
+
+ if (!areEqual(C, enc))
+ {
+ fail("incorrect encrypt in: " + testName);
+ }
+
+ if (!areEqual(tag, encCipher.getMac()))
+ {
+ fail("getMac() not the same as the appended tag: " + testName);
+ }
+ }
+
+ {
+ byte[] dec = new byte[decCipher.getOutputSize(C.length)];
+ int len = decCipher.processBytes(C, 0, C.length, dec, 0);
+ len += decCipher.doFinal(dec, len);
+
+ if (dec.length != len)
+ {
+ fail("decryption reported incorrect length: " + testName);
+ }
+
+ if (!areEqual(P, dec))
+ {
+ fail("incorrect decrypt in: " + testName);
+ }
+
+ if (!areEqual(tag, decCipher.getMac()))
+ {
+ fail("getMac() not the same as the appended tag: " + testName);
+ }
+ }
+ }
+
+ private void runLongerTestCase(int keyLen, int tagLen, String expectedOutputHex)
+ throws InvalidCipherTextException
+ {
+ byte[] expectedOutput = Hex.decode(expectedOutputHex);
+ byte[] keyBytes = new byte[keyLen / 8];
+ keyBytes[keyBytes.length - 1] = (byte)tagLen;
+ KeyParameter key = new KeyParameter(keyBytes);
+
+ AEADBlockCipher c1 = initOCBCipher(true, new AEADParameters(key, tagLen, createNonce(385)));
+ AEADBlockCipher c2 = createOCBCipher();
+
+ long total = 0;
+
+ byte[] S = new byte[128];
+
+ int n = 0;
+ for (int i = 0; i < 128; ++i)
+ {
+ c2.init(true, new AEADParameters(key, tagLen, createNonce(++n)));
+ total += updateCiphers(c1, c2, S, i, true, true);
+ c2.init(true, new AEADParameters(key, tagLen, createNonce(++n)));
+ total += updateCiphers(c1, c2, S, i, false, true);
+ c2.init(true, new AEADParameters(key, tagLen, createNonce(++n)));
+ total += updateCiphers(c1, c2, S, i, true, false);
+ }
+
+ long expectedTotal = 16256 + (48 * tagLen);
+
+ if (total != expectedTotal)
+ {
+ fail("test generated the wrong amount of input: " + total);
+ }
+
+ byte[] output = new byte[c1.getOutputSize(0)];
+ c1.doFinal(output, 0);
+
+ if (!areEqual(expectedOutput, output))
+ {
+ fail("incorrect encrypt in long-form test");
+ }
+ }
+
+ private byte[] createNonce(int n)
+ {
+ return new byte[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte)(n >>> 8), (byte)n };
+ }
+
+ private int updateCiphers(AEADBlockCipher c1, AEADBlockCipher c2, byte[] S, int i,
+ boolean includeAAD, boolean includePlaintext)
+ throws InvalidCipherTextException
+ {
+ int inputLen = includePlaintext ? i : 0;
+ int outputLen = c2.getOutputSize(inputLen);
+
+ byte[] output = new byte[outputLen];
+
+ int len = 0;
+
+ if (includeAAD)
+ {
+ c2.processAADBytes(S, 0, i);
+ }
+
+ if (includePlaintext)
+ {
+ len += c2.processBytes(S, 0, i, output, len);
+ }
+
+ len += c2.doFinal(output, len);
+
+ c1.processAADBytes(output, 0, len);
+
+ return len;
+ }
+
+ private void randomTests()
+ throws InvalidCipherTextException
+ {
+ SecureRandom srng = new SecureRandom();
+ srng.setSeed(Times.nanoTime());
+ for (int i = 0; i < 10; ++i)
+ {
+ randomTest(srng);
+ }
+ }
+
+ private void randomTest(SecureRandom srng)
+ throws InvalidCipherTextException
+ {
+ int kLength = 16 + 8 * (Math.abs(srng.nextInt()) % 3);
+ byte[] K = new byte[kLength];
+ srng.nextBytes(K);
+
+ int pLength = srng.nextInt() >>> 16;
+ byte[] P = new byte[pLength];
+ srng.nextBytes(P);
+
+ int aLength = srng.nextInt() >>> 24;
+ byte[] A = new byte[aLength];
+ srng.nextBytes(A);
+
+ int saLength = srng.nextInt() >>> 24;
+ byte[] SA = new byte[saLength];
+ srng.nextBytes(SA);
+
+ int ivLength = 1 + nextInt(srng, 15);
+ byte[] IV = new byte[ivLength];
+ srng.nextBytes(IV);
+
+ AEADParameters parameters = new AEADParameters(new KeyParameter(K), 16 * 8, IV, A);
+ AEADBlockCipher cipher = initOCBCipher(true, parameters);
+ byte[] C = new byte[cipher.getOutputSize(P.length)];
+ int predicted = cipher.getUpdateOutputSize(P.length);
+
+ int split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ int len = cipher.processBytes(P, 0, P.length, C, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ if (predicted != len)
+ {
+ fail("encryption reported incorrect update length in randomised test");
+ }
+
+ len += cipher.doFinal(C, len);
+
+ if (C.length != len)
+ {
+ fail("encryption reported incorrect length in randomised test");
+ }
+
+ byte[] encT = cipher.getMac();
+ byte[] tail = new byte[C.length - P.length];
+ System.arraycopy(C, P.length, tail, 0, tail.length);
+
+ if (!areEqual(encT, tail))
+ {
+ fail("stream contained wrong mac in randomised test");
+ }
+
+ cipher.init(false, parameters);
+ byte[] decP = new byte[cipher.getOutputSize(C.length)];
+ predicted = cipher.getUpdateOutputSize(C.length);
+
+ split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ len = cipher.processBytes(C, 0, C.length, decP, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ if (predicted != len)
+ {
+ fail("decryption reported incorrect update length in randomised test");
+ }
+
+ len += cipher.doFinal(decP, len);
+
+ if (!areEqual(P, decP))
+ {
+ fail("incorrect decrypt in randomised test");
+ }
+
+ byte[] decT = cipher.getMac();
+ if (!areEqual(encT, decT))
+ {
+ fail("decryption produced different mac from encryption");
+ }
+
+ //
+ // key reuse test
+ //
+ cipher.init(false, AEADTestUtil.reuseKey(parameters));
+ decP = new byte[cipher.getOutputSize(C.length)];
+
+ split = nextInt(srng, SA.length + 1);
+ cipher.processAADBytes(SA, 0, split);
+ len = cipher.processBytes(C, 0, C.length, decP, 0);
+ cipher.processAADBytes(SA, split, SA.length - split);
+
+ len += cipher.doFinal(decP, len);
+
+ if (!areEqual(P, decP))
+ {
+ fail("incorrect decrypt in randomised test");
+ }
+
+ decT = cipher.getMac();
+ if (!areEqual(encT, decT))
+ {
+ fail("decryption produced different mac from encryption");
+ }
+ }
+
+ private void outputSizeTests()
+ {
+ byte[] K = new byte[16];
+ byte[] A = null;
+ byte[] IV = new byte[15];
+
+ AEADParameters parameters = new AEADParameters(new KeyParameter(K), 16 * 8, IV, A);
+ AEADBlockCipher cipher = initOCBCipher(true, parameters);
+
+ if (cipher.getUpdateOutputSize(0) != 0)
+ {
+ fail("incorrect getUpdateOutputSize for initial 0 bytes encryption");
+ }
+
+ if (cipher.getOutputSize(0) != 16)
+ {
+ fail("incorrect getOutputSize for initial 0 bytes encryption");
+ }
+
+ cipher.init(false, parameters);
+
+ if (cipher.getUpdateOutputSize(0) != 0)
+ {
+ fail("incorrect getUpdateOutputSize for initial 0 bytes decryption");
+ }
+
+ // NOTE: 0 bytes would be truncated data, but we want it to fail in the doFinal, not here
+ if (cipher.getOutputSize(0) != 0)
+ {
+ fail("fragile getOutputSize for initial 0 bytes decryption");
+ }
+
+ if (cipher.getOutputSize(16) != 0)
+ {
+ fail("incorrect getOutputSize for initial MAC-size bytes decryption");
+ }
+ }
+
+ private static int nextInt(SecureRandom rand, int n)
+ {
+ if ((n & -n) == n) // i.e., n is a power of 2
+ {
+ return (int)((n * (long)(rand.nextInt() >>> 1)) >> 31);
+ }
+
+ int bits, value;
+ do
+ {
+ bits = rand.nextInt() >>> 1;
+ value = bits % n;
+ }
+ while (bits - value + (n - 1) < 0);
+
+ return value;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new OCBTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenBSDBCryptTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenBSDBCryptTest.java
new file mode 100644
index 00000000..1355f3a5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/OpenBSDBCryptTest.java
@@ -0,0 +1,141 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.generators.OpenBSDBCrypt;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class OpenBSDBCryptTest
+ extends SimpleTest
+{
+
+ private final static String[][] bcryptTest1 = // vectors from http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/glibc/crypt_blowfish/wrapper.c?rev=HEAD
+ {
+ {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW",
+ "U*U"},
+ {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK",
+ "U*U*"},
+ {"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a",
+ "U*U*U"},
+ {"$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
+ ""},
+ {"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
+ "0123456789abcdefghijklmnopqrstuvwxyz"
+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ + "chars after 72 are ignored"},
+ };
+
+ private final static String[] bcryptTest2 = { // from: http://openwall.info/wiki/john/sample-hashes
+ "$2a$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe", "password"
+ };
+
+ private final static String[] bcryptTest2b = { // from: http://stackoverflow.com/questions/11654684/verifying-a-bcrypt-hash
+ "$2a$10$.TtQJ4Jr6isd4Hp.mVfZeuh6Gws4rOQ/vdBczhDx.19NFK0Y84Dle", "ππππππππ"
+ };
+
+ private final static String[][] bcryptTest3 = // from: https://bitbucket.org/vadim/bcrypt.net/src/464c41416dc9/BCrypt.Net.Test/TestBCrypt.cs - plain - salt - expected
+ {
+ {"", "$2a$06$DCq7YPn5Rq63x1Lad4cll.", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."},
+ {"", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"},
+ {"", "$2a$10$k1wbIrmNyFAPwPVPSVa/ze", "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW"},
+ {"", "$2a$12$k42ZFHFWqBp3vWli.nIn8u", "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO"},
+ {"a", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"},
+ {"a", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfe", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V."},
+ {"a", "$2a$10$k87L/MF28Q673VKh8/cPi.", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"},
+ {"a", "$2a$12$8NJH3LsPrANStV6XtBakCe", "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS"},
+ {"abc", "$2a$06$If6bvum7DFjUnE9p2uDeDu", "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"},
+ {"abc", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7O", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm"},
+ {"abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"},
+ {"abc", "$2a$12$EXRkfkdmXn2gzds2SSitu.", "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q"},
+ {"abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGu", "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"},
+ {"abcdefghijklmnopqrstuvwxyz", "$2a$08$aTsUwsyowQuzRrDqFflhge", "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."},
+ {"abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1u", "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"},
+ {"abcdefghijklmnopqrstuvwxyz", "$2a$12$D4G5f18o7aMMfwasBL7Gpu", "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"},
+ {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$06$fPIsBO8qRqkjj273rfaOI.", "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO"},
+ {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$08$Eq2r4G/76Wv39MzSX262hu", "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW"},
+ {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"},
+ {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPO", "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"},
+ };
+
+ private static final String[][] bcryptTest4 = { // from: https://github.com/ChrisMcKee/cryptsharp/blob/master/Tests/vectors/BCrypt.txt
+ {"n6HyjrYTo/r4lgjvM7L<`iM", "$2a$07$XPrYfnqc5ankSHnRfmPVu.A0trKq3VdczdbJjKaWIksKF.GfFCxv."},
+ {"~s0quB/K8zRtRT:QtZr`s|^O", "$2a$07$5zzz8omiaStXwOetWwlmuePPRwUt0jhNBPYGGgAMcUDvqsGVqv9Cy"},
+ {"r>8y3uE}6<7nI34?Q2rR0JEw", "$2a$07$k5AH9bO9aplPYdZMZ155qOcY1FewMXcupWewW6fViUtsVQ2Umg6LS"},
+ {">l_7}xxH3|Cr{dCR[HTUN@k~", "$2a$05$24xz81ZZsMUMm940bbWMCeHsO.s6A3MG0JZzm4y3.Ti6P96bz6RN6"},
+ {"D`lCFYTe9_8IW6nEB:oPjEk/S", "$2a$05$bA1xkp4NqFvDmtQJtDO9CugW0INxQLpMZha8AaHmBj9Zg9HlfQtBa"},
+ {"UBGYU6|a|RpA:bp[;}p.ZY4f1", "$2a$08$gu4KBnkla.bEqHiwaJ8.z.0ixfzE1Q0/iPfmpfRmUA.NUhUdZboxa"},
+ {"O9X[kP6{63F3rXKtN>n?zh2_", "$2a$04$yRZW9xEsqN9DL19jveqFyO1bljZ0r5KNCYqQzMqYpDB7XHWqDWNGC"},
+ {":Sa:BknepsG}\\5dOj>kh0KAk", "$2a$04$KhDTFUlakUsPNuLQSgyr7.xQZxkTSIvo0nFw0XyjvrH6n5kZkYDLG"},// extra escape sequence added
+ {"2_9J6k:{z?SSjCzL/GT/5CMgc", "$2a$05$eN1jCXDxN9HmuIARJlwH4ewsEyYbAmq7Cw99gEHqGRXtWyrRNLScy"},
+
+ {"2KNy`Kodau14?s8XVru<IIw0eDw|.64MM^Wtv;3sfZt~3`2QN6/U]0^1HtETqWHt<lMfD-LX::zo7AcNLQ.Q.@.g5kX`j7hRi", "$2a$04$xUNE1aUuNlpNwSOuz1VpjuBgW95ImLccIquQxyGLeinucvokg2Ale"},
+ {"0yWE>E;h/kdCRd@T]fQiv`Vz]KC0zaIAIeyY4zcooQ0^DfP{hHsw9?atO}CxbkbnK-LxUe;|FiBEluVqO@ysHhXQDdXPt0p", "$2a$07$pNHi/IxrSUohtsD5/eIv4O324ZPGfJE7mUAaNpIPkpyxjW9kqIk76"},
+ {"ilWj~2mLBa1Pq`sxrW8fNNq:XF0@KP5RLW9u?[E_wwkROmCSWudYoS5I2HGI-1-?Pd0zVxTIeNbF;nLDUGtce{8dHmx90:;N<8", "$2a$07$ePVgkQl8QKSG2Xv6o0bnOe4SZp4ejag5CP44tjxfmY17F5VzRgwF6"},
+ {"dj~OsXmQGj6FXnPGgwg9]G@75~L@G[|e<hgh2vaNqIyYZPh@M;I1DTgZS/~Q:i[6d]oei:hBw4}{}y7k9K^4SoN}wb8mrg[", "$2a$04$BZT7YoAYAgtNkD0/BOl.jOi0dDni7WtmB8.wAebHeHkOs.TpRgml."},
+ {"7;PjW]RYJoZXf.r2M^Mm1jVIe0wJ=Kdd2iUBuu1v3HGI1-S[TB6yg{0~:nbpeA08dysS5d}@Oxbrpj[~i-60mpq1WZqQmSVpnR", "$2a$07$fa9NDzoPKiSWC67cP/tj2OqE0PqvGwzRoJiCKj.czyqKyvpdtVpKe"},
+ {"8nv;PAN~-FQ]Emh@.TKG=^.t8R0EQC0T?x9|9g4xzxYmSbBO1qDx8kv-ehh0IBv>3KWhz.Z~jUF0tt8[5U@8;5:=[v6pf.IEJ", "$2a$08$eXo9KDc1BZyybBgMurpcD.GA1/ch3XhgBnIH10Xvjc2ogZaGg3t/m"},
+ };
+
+ public String getName()
+ {
+ return "OpenBSDBCrypt";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i < bcryptTest1.length; i++)
+ {
+ String[] testString = bcryptTest1[i];
+ String encoded = testString[0];
+ String password = testString[1];
+ if (!OpenBSDBCrypt.checkPassword(encoded, password.toCharArray()))
+ {
+ fail("test1 mismatch: " + "[" + i + "] " + password);
+ }
+ }
+
+ String encoded = bcryptTest2[0];
+ String password = bcryptTest2[1];
+ if (!OpenBSDBCrypt.checkPassword(encoded, password.toCharArray()))
+ {
+ fail("bcryptTest2 mismatch: " + password);
+ }
+
+
+ encoded = bcryptTest2b[0];
+ password = bcryptTest2b[1];
+ if (!OpenBSDBCrypt.checkPassword(encoded, password.toCharArray()))
+ {
+ fail("bcryptTest2b mismatch: " + password);
+ }
+
+
+ for (int i = 0; i < bcryptTest3.length; i++)
+ {
+ String[] testString = bcryptTest3[i];
+ encoded = testString[2];
+ password = testString[0];
+ if (!OpenBSDBCrypt.checkPassword(encoded, password.toCharArray()))
+ {
+ fail("test3 mismatch: " + "[" + i + "] " + password);
+ }
+ }
+
+
+ for (int i = 0; i < bcryptTest4.length; i++)
+ {
+ String[] testString = bcryptTest4[i];
+ encoded = testString[1];
+ password = testString[0];
+ if (!OpenBSDBCrypt.checkPassword(encoded, password.toCharArray()))
+ {
+ fail("test4 mismatch: " + "[" + i + "] " + password);
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new OpenBSDBCryptTest());
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/PKCS12Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/PKCS12Test.java
new file mode 100644
index 00000000..c5c7aa34
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/PKCS12Test.java
@@ -0,0 +1,206 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * test for PKCS12 key generation - vectors from
+ * <a href=http://www.drh-consultancy.demon.co.uk/test.txt>
+ * http://www.drh-consultancy.demon.co.uk/test.txt</a>
+ */
+public class PKCS12Test
+ implements Test
+{
+ char[] password1 = { 's', 'm', 'e', 'g' };
+ char[] password2 = { 'q', 'u', 'e', 'e', 'g' };
+
+ private boolean isEqual(
+ byte[] a,
+ byte[] b)
+ {
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private TestResult run1(
+ int id,
+ char[] password,
+ byte[] salt,
+ int iCount,
+ byte[] result)
+ {
+ PBEParametersGenerator generator = new PKCS12ParametersGenerator(
+ new SHA1Digest());
+
+ generator.init(
+ PBEParametersGenerator.PKCS12PasswordToBytes(password),
+ salt,
+ iCount);
+
+ CipherParameters key = generator.generateDerivedParameters(24 * 8);
+
+ if (isEqual(result, ((KeyParameter)key).getKey()))
+ {
+ return new SimpleTestResult(true, "PKCS12Test: Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, "PKCS12Test: id "
+ + id + " Failed");
+ }
+ }
+
+ private TestResult run2(
+ int id,
+ char[] password,
+ byte[] salt,
+ int iCount,
+ byte[] result)
+ {
+ PBEParametersGenerator generator = new PKCS12ParametersGenerator(
+ new SHA1Digest());
+
+ generator.init(
+ PBEParametersGenerator.PKCS12PasswordToBytes(password),
+ salt,
+ iCount);
+
+ ParametersWithIV params = (ParametersWithIV)generator.generateDerivedParameters(64, 64);
+
+ if (isEqual(result, params.getIV()))
+ {
+ return new SimpleTestResult(true, "PKCS12Test: Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, "PKCS12Test: id "
+ + id + " Failed");
+ }
+ }
+
+ private TestResult run3(
+ int id,
+ char[] password,
+ byte[] salt,
+ int iCount,
+ byte[] result)
+ {
+ PBEParametersGenerator generator = new PKCS12ParametersGenerator(
+ new SHA1Digest());
+
+ generator.init(
+ PBEParametersGenerator.PKCS12PasswordToBytes(password),
+ salt,
+ iCount);
+
+ CipherParameters key = generator.generateDerivedMacParameters(160);
+
+ if (isEqual(result, ((KeyParameter)key).getKey()))
+ {
+ return new SimpleTestResult(true, "PKCS12Test: Okay");
+ }
+ else
+ {
+ return new SimpleTestResult(false, "PKCS12Test: id "
+ + id + " Failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "PKCS12Test";
+ }
+
+ public TestResult perform()
+ {
+ TestResult result;
+
+ result = run1(1, password1, Hex.decode("0A58CF64530D823F"), 1,
+ Hex.decode("8AAAE6297B6CB04642AB5B077851284EB7128F1A2A7FBCA3"));
+
+ if (result.isSuccessful())
+ {
+ result = run2(2, password1, Hex.decode("0A58CF64530D823F"), 1,
+ Hex.decode("79993DFE048D3B76"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run1(3, password1, Hex.decode("642B99AB44FB4B1F"), 1,
+ Hex.decode("F3A95FEC48D7711E985CFE67908C5AB79FA3D7C5CAA5D966"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run2(4, password1, Hex.decode("642B99AB44FB4B1F"), 1,
+ Hex.decode("C0A38D64A79BEA1D"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run3(5, password1, Hex.decode("3D83C0E4546AC140"), 1,
+ Hex.decode("8D967D88F6CAA9D714800AB3D48051D63F73A312"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run1(6, password2, Hex.decode("05DEC959ACFF72F7"), 1000,
+ Hex.decode("ED2034E36328830FF09DF1E1A07DD357185DAC0D4F9EB3D4"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run2(7, password2, Hex.decode("05DEC959ACFF72F7"), 1000,
+ Hex.decode("11DEDAD7758D4860"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run1(8, password2, Hex.decode("1682C0FC5B3F7EC5"), 1000,
+ Hex.decode("483DD6E919D7DE2E8E648BA8F862F3FBFBDC2BCB2C02957F"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run2(9, password2, Hex.decode("1682C0FC5B3F7EC5"), 1000,
+ Hex.decode("9D461D1B00355C50"));
+ }
+
+ if (result.isSuccessful())
+ {
+ result = run3(10, password2, Hex.decode("263216FCC2FAB31C"), 1000,
+ Hex.decode("5EC4C7A80DF652294C3925B6489A7AB857C83476"));
+ }
+
+ return result;
+ }
+
+ public static void main(
+ String[] args)
+ {
+ PKCS12Test test = new PKCS12Test();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/PKCS5Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/PKCS5Test.java
new file mode 100644
index 00000000..6145114e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/PKCS5Test.java
@@ -0,0 +1,265 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.ByteArrayInputStream;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.EncryptionScheme;
+import org.bouncycastle.asn1.pkcs.KeyDerivationFunc;
+import org.bouncycastle.asn1.pkcs.PBES2Parameters;
+import org.bouncycastle.asn1.pkcs.PBKDF2Params;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.RC2Engine;
+import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * A test class for PKCS5 PBES2 with PBKDF2 (PKCS5 v2.0) using
+ * test vectors provider at
+ * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html>
+ * RSA's PKCS5 Page</a>
+ * <br>
+ * The vectors are Base 64 encoded and encrypted using the password "password"
+ * (without quotes). They should all yield the same PrivateKeyInfo object.
+ */
+public class PKCS5Test
+ extends SimpleTest
+{
+ /**
+ * encrypted using des-cbc.
+ */
+ static byte[] sample1 = Base64.decode(
+ "MIIBozA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIfWBDXwLp4K4CAggA"
+ + "MBEGBSsOAwIHBAiaCF/AvOgQ6QSCAWDWX4BdAzCRNSQSANSuNsT5X8mWYO27mr3Y"
+ + "9c9LoBVXGNmYWKA77MI4967f7SmjNcgXj3xNE/jmnVz6hhsjS8E5VPT3kfyVkpdZ"
+ + "0lr5e9Yk2m3JWpPU7++v5zBkZmC4V/MwV/XuIs6U+vykgzMgpxQg0oZKS9zgmiZo"
+ + "f/4dOCL0UtCDnyOSvqT7mCVIcMDIEKu8QbVlgZYBop08l60EuEU3gARUo8WsYQmO"
+ + "Dz/ldx0Z+znIT0SXVuOwc+RVItC5T/Qx+aijmmpt+9l14nmaGBrEkmuhmtdvU/4v"
+ + "aptewGRgmjOfD6cqK+zs0O5NrrJ3P/6ZSxXj91CQgrThGfOv72bUncXEMNtc8pks"
+ + "2jpHFjGMdKufnadAD7XuMgzkkaklEXZ4f5tU6heIIwr51g0GBEGF96gYPFnjnSQM"
+ + "75JE02Clo+DfcfXpcybPTwwFg2jd6JTTOfkdf6OdSlA/1XNK43FA");
+
+ /**
+ * encrypted using des-ede3-cbc.
+ */
+ static byte[] sample2 = Base64.decode(
+ "MIIBpjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIeFeOWl1jywYCAggA"
+ + "MBQGCCqGSIb3DQMHBAjUJ5eGBhQGtQSCAWBrHrRgqO8UUMLcWzZEtpk1l3mjxiF/"
+ + "koCMkHsFwowgyWhEbgIkTgbSViK54LVK8PskekcGNLph+rB6bGZ7pPbL5pbXASJ8"
+ + "+MkQcG3FZdlS4Ek9tTJDApj3O1UubZGFG4uvTlJJFbF1BOJ3MkY3XQ9Gl1qwv7j5"
+ + "6e103Da7Cq9+oIDKmznza78XXQYrUsPo8mJGjUxPskEYlzwvHjKubRnYm/K6RKhi"
+ + "5f4zX4BQ/Dt3H812ZjRXrsjAJP0KrD/jyD/jCT7zNBVPH1izBds+RwizyQAHwfNJ"
+ + "BFR78TH4cgzB619X47FDVOnT0LqQNVd0O3cSwnPrXE9XR3tPayE+iOB15llFSmi8"
+ + "z0ByOXldEpkezCn92Umk++suzIVj1qfsK+bv2phZWJPbLEIWPDRHUbYf76q5ArAr"
+ + "u4xtxT/hoK3krEs/IN3d70qjlUJ36SEw1UaZ82PWhakQbdtu39ZraMJB");
+
+ /**
+ * encrypted using rc2-cbc.
+ */
+ static byte[] sample3 = Base64.decode(
+ "MIIBrjBIBgkqhkiG9w0BBQ0wOzAeBgkqhkiG9w0BBQwwEQQIrHyQPBZqWLUCAggA"
+ + "AgEQMBkGCCqGSIb3DQMCMA0CAToECEhbh7YZKiPSBIIBYCT1zp6o5jpFlIkgwPop"
+ + "7bW1+8ACr4exqzkeb3WflQ8cWJ4cURxzVdvxUnXeW1VJdaQZtjS/QHs5GhPTG/0f"
+ + "wtvnaPfwrIJ3FeGaZfcg2CrYhalOFmEb4xrE4KyoEQmUN8tb/Cg94uzd16BOPw21"
+ + "RDnE8bnPdIGY7TyL95kbkqH23mK53pi7h+xWIgduW+atIqDyyt55f7WMZcvDvlj6"
+ + "VpN/V0h+qxBHL274WA4dj6GYgeyUFpi60HdGCK7By2TBy8h1ZvKGjmB9h8jZvkx1"
+ + "MkbRumXxyFsowTZawyYvO8Um6lbfEDP9zIEUq0IV8RqH2MRyblsPNSikyYhxX/cz"
+ + "tdDxRKhilySbSBg5Kr8OfcwKp9bpinN96nmG4xr3Tch1bnVvqJzOQ5+Vva2WwVvH"
+ + "2JkWvYm5WaANg4Q6bRxu9vz7DuhbJjQdZbxFezIAgrJdSe92B00jO/0Kny1WjiVO"
+ + "6DA=");
+
+ static byte[] result = Hex.decode(
+ "30820155020100300d06092a864886f70d01010105000482013f3082013b020100024100"
+ + "debbfc2c09d61bada2a9462f24224e54cc6b3cc0755f15ce318ef57e79df17026b6a85cc"
+ + "a12428027245045df2052a329a2f9ad3d17b78a10572ad9b22bf343b020301000102402d"
+ + "90a96adcec472743527bc023153d8f0d6e96b40c8ed228276d467d843306429f8670559b"
+ + "f376dd41857f6397c2fc8d95e0e53ed62de420b855430ee4a1b8a1022100ffcaf0838239"
+ + "31e073ff534f06a5d415b3d414bc614a4544a3dff7ed271817eb022100deea30242117db"
+ + "2d3b8837f58f1da530ff83cf9283680da33683ec4e583610f1022100e6026381adb0a683"
+ + "f16a8f4c096b462979b9e4277cc89f3ed8a905b46fa9ff9f02210097c146d4d1d2b3dbaf"
+ + "53a504ff51674c5c271800de84d003f4f10ac6ab36e38102202bfa141f10bda874e1017d"
+ + "845e82767c1c38e82745daf421f0c8cd09d7652387");
+
+ private class PBETest
+ extends SimpleTest
+ {
+ int id;
+ BufferedBlockCipher cipher;
+ byte[] sample;
+ int keySize;
+
+ PBETest(
+ int id,
+ BufferedBlockCipher cipher,
+ byte[] sample,
+ int keySize)
+ {
+ this.id = id;
+ this.cipher = cipher;
+ this.sample = sample;
+ this.keySize = keySize;
+ }
+
+ public String getName()
+ {
+ return cipher.getUnderlyingCipher().getAlgorithmName() + " PKCS5S2 Test " + id;
+ }
+
+ public void performTest()
+ {
+ char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
+ PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
+ ByteArrayInputStream bIn = new ByteArrayInputStream(sample);
+ ASN1InputStream dIn = new ASN1InputStream(bIn);
+ EncryptedPrivateKeyInfo info = null;
+
+ try
+ {
+ info = EncryptedPrivateKeyInfo.getInstance(dIn.readObject());
+ }
+ catch (Exception e)
+ {
+ fail("failed construction - exception " + e.toString(), e);
+ }
+
+ PBES2Parameters alg = PBES2Parameters.getInstance(info.getEncryptionAlgorithm().getParameters());
+ PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
+ EncryptionScheme scheme = alg.getEncryptionScheme();
+
+ if (func.getKeyLength() != null)
+ {
+ keySize = func.getKeyLength().intValue() * 8;
+ }
+
+ int iterationCount = func.getIterationCount().intValue();
+ byte[] salt = func.getSalt();
+
+ generator.init(
+ PBEParametersGenerator.PKCS5PasswordToBytes(password),
+ salt,
+ iterationCount);
+
+ CipherParameters param;
+
+ if (scheme.getAlgorithm().equals(PKCSObjectIdentifiers.RC2_CBC))
+ {
+ RC2CBCParameter rc2Params = RC2CBCParameter.getInstance(scheme.getParameters());
+ byte[] iv = rc2Params.getIV();
+
+ param = new ParametersWithIV(generator.generateDerivedParameters(keySize), iv);
+ }
+ else
+ {
+ byte[] iv = ASN1OctetString.getInstance(scheme.getParameters()).getOctets();
+
+ param = new ParametersWithIV(generator.generateDerivedParameters(keySize), iv);
+ }
+
+ cipher.init(false, param);
+
+ byte[] data = info.getEncryptedData();
+ byte[] out = new byte[cipher.getOutputSize(data.length)];
+ int len = cipher.processBytes(data, 0, data.length, out, 0);
+
+ try
+ {
+ len += cipher.doFinal(out, len);
+ }
+ catch (Exception e)
+ {
+ fail("failed doFinal - exception " + e.toString());
+ }
+
+ if (result.length != len)
+ {
+ fail("failed length");
+ }
+
+ for (int i = 0; i != len; i++)
+ {
+ if (out[i] != result[i])
+ {
+ fail("failed comparison");
+ }
+ }
+ }
+ }
+
+ public String getName()
+ {
+ return "PKCS5S2";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESEngine()));
+ SimpleTest test = new PBETest(0, cipher, sample1, 64);
+
+ test.performTest();
+
+ cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
+ test = new PBETest(1, cipher, sample2, 192);
+
+ test.performTest();
+
+ cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RC2Engine()));
+ test = new PBETest(2, cipher, sample3, 0);
+ test.performTest();
+
+ //
+ // RFC 3211 tests
+ //
+ char[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
+ PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
+
+ byte[] salt = Hex.decode("1234567878563412");
+
+ generator.init(
+ PBEParametersGenerator.PKCS5PasswordToBytes(password),
+ salt,
+ 5);
+
+ if (!areEqual(((KeyParameter)generator.generateDerivedParameters(64)).getKey(), Hex.decode("d1daa78615f287e6")))
+ {
+ fail("64 test failed");
+ }
+
+ password = "All n-entities must communicate with other n-entities via n-1 entiteeheehees".toCharArray();
+
+ generator.init(
+ PBEParametersGenerator.PKCS5PasswordToBytes(password),
+ salt,
+ 500);
+
+ if (!areEqual(((KeyParameter)generator.generateDerivedParameters(192)).getKey(), Hex.decode("6a8970bf68c92caea84a8df28510858607126380cc47ab2d")))
+ {
+ fail("192 test failed");
+ }
+
+ generator.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, 60000);
+ if (!areEqual(((KeyParameter)generator.generateDerivedParameters(192)).getKey(), Hex.decode("29aaef810c12ecd2236bbcfb55407f9852b5573dc1c095bb")))
+ {
+ fail("192 (60000) test failed");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PKCS5Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/PSSBlindTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/PSSBlindTest.java
new file mode 100644
index 00000000..5e391ae2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/PSSBlindTest.java
@@ -0,0 +1,398 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.engines.RSABlindingEngine;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.generators.RSABlindingFactorGenerator;
+import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSABlindingParameters;
+import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.crypto.signers.PSSSigner;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/*
+ * RSA PSS test vectors for PKCS#1 V2.1 with blinding
+ */
+public class PSSBlindTest
+ extends SimpleTest
+{
+ private final int DATA_LENGTH = 1000;
+ private final int NUM_TESTS = 50;
+ private final int NUM_TESTS_WITH_KEY_GENERATION = 10;
+
+ private class FixedRandom
+ extends SecureRandom
+ {
+ byte[] vals;
+
+ FixedRandom(
+ byte[] vals)
+ {
+ this.vals = vals;
+ }
+
+ public void nextBytes(
+ byte[] bytes)
+ {
+ System.arraycopy(vals, 0, bytes, 0, vals.length);
+ }
+ }
+
+ //
+ // Example 1: A 1024-bit RSA keypair
+ //
+ private RSAKeyParameters pub1 = new RSAKeyParameters(false,
+ new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
+ new BigInteger("010001",16));
+
+ private RSAKeyParameters prv1 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
+ new BigInteger("010001",16),
+ new BigInteger("33a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325",16),
+ new BigInteger("e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443",16),
+ new BigInteger("b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd",16),
+ new BigInteger("28fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa027861979",16),
+ new BigInteger("1a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729",16),
+ new BigInteger("27156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d",16));
+
+ // PSSExample1.1
+
+ private byte[] msg1a = Hex.decode("cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73318b750a0167d0");
+
+ private byte[] slt1a = Hex.decode("dee959c7e06411361420ff80185ed57f3e6776af");
+
+ private byte[] sig1a = Hex.decode("9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f382a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd500f1ff2b994d8a4e30cbb33c");
+
+ // PSSExample1.2
+
+ private byte[] msg1b = Hex.decode("851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e");
+
+ private byte[] slt1b = Hex.decode("ef2869fa40c346cb183dab3d7bffc98fd56df42d");
+
+ private byte[] sig1b = Hex.decode("3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c729984f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193be5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98c22c4d4d47724fdb5669e843");
+
+ //
+ // Example 2: A 1025-bit RSA keypair
+ //
+
+ private RSAKeyParameters pub2 = new RSAKeyParameters(false,
+ new BigInteger("01d40c1bcf97a68ae7cdbd8a7bf3e34fa19dcca4ef75a47454375f94514d88fed006fb829f8419ff87d6315da68a1ff3a0938e9abb3464011c303ad99199cf0c7c7a8b477dce829e8844f625b115e5e9c4a59cf8f8113b6834336a2fd2689b472cbb5e5cabe674350c59b6c17e176874fb42f8fc3d176a017edc61fd326c4b33c9", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv2 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("01d40c1bcf97a68ae7cdbd8a7bf3e34fa19dcca4ef75a47454375f94514d88fed006fb829f8419ff87d6315da68a1ff3a0938e9abb3464011c303ad99199cf0c7c7a8b477dce829e8844f625b115e5e9c4a59cf8f8113b6834336a2fd2689b472cbb5e5cabe674350c59b6c17e176874fb42f8fc3d176a017edc61fd326c4b33c9", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("027d147e4673057377fd1ea201565772176a7dc38358d376045685a2e787c23c15576bc16b9f444402d6bfc5d98a3e88ea13ef67c353eca0c0ddba9255bd7b8bb50a644afdfd1dd51695b252d22e7318d1b6687a1c10ff75545f3db0fe602d5f2b7f294e3601eab7b9d1cecd767f64692e3e536ca2846cb0c2dd486a39fa75b1", 16),
+ new BigInteger("016601e926a0f8c9e26ecab769ea65a5e7c52cc9e080ef519457c644da6891c5a104d3ea7955929a22e7c68a7af9fcad777c3ccc2b9e3d3650bce404399b7e59d1", 16),
+ new BigInteger("014eafa1d4d0184da7e31f877d1281ddda625664869e8379e67ad3b75eae74a580e9827abd6eb7a002cb5411f5266797768fb8e95ae40e3e8a01f35ff89e56c079", 16),
+ new BigInteger("e247cce504939b8f0a36090de200938755e2444b29539a7da7a902f6056835c0db7b52559497cfe2c61a8086d0213c472c78851800b171f6401de2e9c2756f31", 16),
+ new BigInteger("b12fba757855e586e46f64c38a70c68b3f548d93d787b399999d4c8f0bbd2581c21e19ed0018a6d5d3df86424b3abcad40199d31495b61309f27c1bf55d487c1", 16),
+ new BigInteger("564b1e1fa003bda91e89090425aac05b91da9ee25061e7628d5f51304a84992fdc33762bd378a59f030a334d532bd0dae8f298ea9ed844636ad5fb8cbdc03cad", 16));
+
+ // PSS Example 2.1
+
+ private byte[] msg2a = Hex.decode("daba032066263faedb659848115278a52c44faa3a76f37515ed336321072c40a9d9b53bc05014078adf520875146aae70ff060226dcb7b1f1fc27e9360");
+ private byte[] slt2a = Hex.decode("57bf160bcb02bb1dc7280cf0458530b7d2832ff7");
+ private byte[] sig2a = Hex.decode("014c5ba5338328ccc6e7a90bf1c0ab3fd606ff4796d3c12e4b639ed9136a5fec6c16d8884bdd99cfdc521456b0742b736868cf90de099adb8d5ffd1deff39ba4007ab746cefdb22d7df0e225f54627dc65466131721b90af445363a8358b9f607642f78fab0ab0f43b7168d64bae70d8827848d8ef1e421c5754ddf42c2589b5b3");
+
+ // PSS Example 2.2
+
+ private byte[] msg2b = Hex.decode("e4f8601a8a6da1be34447c0959c058570c3668cfd51dd5f9ccd6ad4411fe8213486d78a6c49f93efc2ca2288cebc2b9b60bd04b1e220d86e3d4848d709d032d1e8c6a070c6af9a499fcf95354b14ba6127c739de1bb0fd16431e46938aec0cf8ad9eb72e832a7035de9b7807bdc0ed8b68eb0f5ac2216be40ce920c0db0eddd3860ed788efaccaca502d8f2bd6d1a7c1f41ff46f1681c8f1f818e9c4f6d91a0c7803ccc63d76a6544d843e084e363b8acc55aa531733edb5dee5b5196e9f03e8b731b3776428d9e457fe3fbcb3db7274442d785890e9cb0854b6444dace791d7273de1889719338a77fe");
+ private byte[] slt2b = Hex.decode("7f6dd359e604e60870e898e47b19bf2e5a7b2a90");
+ private byte[] sig2b = Hex.decode("010991656cca182b7f29d2dbc007e7ae0fec158eb6759cb9c45c5ff87c7635dd46d150882f4de1e9ae65e7f7d9018f6836954a47c0a81a8a6b6f83f2944d6081b1aa7c759b254b2c34b691da67cc0226e20b2f18b42212761dcd4b908a62b371b5918c5742af4b537e296917674fb914194761621cc19a41f6fb953fbcbb649dea");
+
+ //
+ // Example 4: A 1027-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub4 = new RSAKeyParameters(false,
+ new BigInteger("054adb7886447efe6f57e0368f06cf52b0a3370760d161cef126b91be7f89c421b62a6ec1da3c311d75ed50e0ab5fff3fd338acc3aa8a4e77ee26369acb81ba900fa83f5300cf9bb6c53ad1dc8a178b815db4235a9a9da0c06de4e615ea1277ce559e9c108de58c14a81aa77f5a6f8d1335494498848c8b95940740be7bf7c3705", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv4 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("054adb7886447efe6f57e0368f06cf52b0a3370760d161cef126b91be7f89c421b62a6ec1da3c311d75ed50e0ab5fff3fd338acc3aa8a4e77ee26369acb81ba900fa83f5300cf9bb6c53ad1dc8a178b815db4235a9a9da0c06de4e615ea1277ce559e9c108de58c14a81aa77f5a6f8d1335494498848c8b95940740be7bf7c3705", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("fa041f8cd9697ceed38ec8caa275523b4dd72b09a301d3541d72f5d31c05cbce2d6983b36183af10690bd46c46131e35789431a556771dd0049b57461bf060c1f68472e8a67c25f357e5b6b4738fa541a730346b4a07649a2dfa806a69c975b6aba64678acc7f5913e89c622f2d8abb1e3e32554e39df94ba60c002e387d9011", 16),
+ new BigInteger("029232336d2838945dba9dd7723f4e624a05f7375b927a87abe6a893a1658fd49f47f6c7b0fa596c65fa68a23f0ab432962d18d4343bd6fd671a5ea8d148413995", 16),
+ new BigInteger("020ef5efe7c5394aed2272f7e81a74f4c02d145894cb1b3cab23a9a0710a2afc7e3329acbb743d01f680c4d02afb4c8fde7e20930811bb2b995788b5e872c20bb1", 16),
+ new BigInteger("026e7e28010ecf2412d9523ad704647fb4fe9b66b1a681581b0e15553a89b1542828898f27243ebab45ff5e1acb9d4df1b051fbc62824dbc6f6c93261a78b9a759", 16),
+ new BigInteger("012ddcc86ef655998c39ddae11718669e5e46cf1495b07e13b1014cd69b3af68304ad2a6b64321e78bf3bbca9bb494e91d451717e2d97564c6549465d0205cf421", 16),
+ new BigInteger("010600c4c21847459fe576703e2ebecae8a5094ee63f536bf4ac68d3c13e5e4f12ac5cc10ab6a2d05a199214d1824747d551909636b774c22cac0b837599abcc75", 16));
+
+ // PSS Example 4.1
+
+ private byte[] msg4a = Hex.decode("9fb03b827c8217d9");
+
+ private byte[] slt4a = Hex.decode("ed7c98c95f30974fbe4fbddcf0f28d6021c0e91d");
+
+ private byte[] sig4a = Hex.decode("0323d5b7bf20ba4539289ae452ae4297080feff4518423ff4811a817837e7d82f1836cdfab54514ff0887bddeebf40bf99b047abc3ecfa6a37a3ef00f4a0c4a88aae0904b745c846c4107e8797723e8ac810d9e3d95dfa30ff4966f4d75d13768d20857f2b1406f264cfe75e27d7652f4b5ed3575f28a702f8c4ed9cf9b2d44948");
+
+ // PSS Example 4.2
+
+ private byte[] msg4b = Hex.decode("0ca2ad77797ece86de5bf768750ddb5ed6a3116ad99bbd17edf7f782f0db1cd05b0f677468c5ea420dc116b10e80d110de2b0461ea14a38be68620392e7e893cb4ea9393fb886c20ff790642305bf302003892e54df9f667509dc53920df583f50a3dd61abb6fab75d600377e383e6aca6710eeea27156e06752c94ce25ae99fcbf8592dbe2d7e27453cb44de07100ebb1a2a19811a478adbeab270f94e8fe369d90b3ca612f9f");
+
+ private byte[] slt4b = Hex.decode("22d71d54363a4217aa55113f059b3384e3e57e44");
+
+ private byte[] sig4b = Hex.decode("049d0185845a264d28feb1e69edaec090609e8e46d93abb38371ce51f4aa65a599bdaaa81d24fba66a08a116cb644f3f1e653d95c89db8bbd5daac2709c8984000178410a7c6aa8667ddc38c741f710ec8665aa9052be929d4e3b16782c1662114c5414bb0353455c392fc28f3db59054b5f365c49e1d156f876ee10cb4fd70598");
+
+
+ //
+ // Example 8: A 1031-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub8 = new RSAKeyParameters(false,
+ new BigInteger("495370a1fb18543c16d3631e3163255df62be6eee890d5f25509e4f778a8ea6fbbbcdf85dff64e0d972003ab3681fbba6dd41fd541829b2e582de9f2a4a4e0a2d0900bef4753db3cee0ee06c7dfae8b1d53b5953218f9cceea695b08668edeaadced9463b1d790d5ebf27e9115b46cad4d9a2b8efab0561b0810344739ada0733f", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv8 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("495370a1fb18543c16d3631e3163255df62be6eee890d5f25509e4f778a8ea6fbbbcdf85dff64e0d972003ab3681fbba6dd41fd541829b2e582de9f2a4a4e0a2d0900bef4753db3cee0ee06c7dfae8b1d53b5953218f9cceea695b08668edeaadced9463b1d790d5ebf27e9115b46cad4d9a2b8efab0561b0810344739ada0733f", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("6c66ffe98980c38fcdeab5159898836165f4b4b817c4f6a8d486ee4ea9130fe9b9092bd136d184f95f504a607eac565846d2fdd6597a8967c7396ef95a6eeebb4578a643966dca4d8ee3de842de63279c618159c1ab54a89437b6a6120e4930afb52a4ba6ced8a4947ac64b30a3497cbe701c2d6266d517219ad0ec6d347dbe9", 16),
+ new BigInteger("08dad7f11363faa623d5d6d5e8a319328d82190d7127d2846c439b0ab72619b0a43a95320e4ec34fc3a9cea876422305bd76c5ba7be9e2f410c8060645a1d29edb", 16),
+ new BigInteger("0847e732376fc7900f898ea82eb2b0fc418565fdae62f7d9ec4ce2217b97990dd272db157f99f63c0dcbb9fbacdbd4c4dadb6df67756358ca4174825b48f49706d", 16),
+ new BigInteger("05c2a83c124b3621a2aa57ea2c3efe035eff4560f33ddebb7adab81fce69a0c8c2edc16520dda83d59a23be867963ac65f2cc710bbcfb96ee103deb771d105fd85", 16),
+ new BigInteger("04cae8aa0d9faa165c87b682ec140b8ed3b50b24594b7a3b2c220b3669bb819f984f55310a1ae7823651d4a02e99447972595139363434e5e30a7e7d241551e1b9", 16),
+ new BigInteger("07d3e47bf686600b11ac283ce88dbb3f6051e8efd04680e44c171ef531b80b2b7c39fc766320e2cf15d8d99820e96ff30dc69691839c4b40d7b06e45307dc91f3f", 16));
+
+ // PSS Example 8.1
+
+ private byte[] msg8a = Hex.decode("81332f4be62948415ea1d899792eeacf6c6e1db1da8be13b5cea41db2fed467092e1ff398914c714259775f595f8547f735692a575e6923af78f22c6997ddb90fb6f72d7bb0dd5744a31decd3dc3685849836ed34aec596304ad11843c4f88489f209735f5fb7fdaf7cec8addc5818168f880acbf490d51005b7a8e84e43e54287977571dd99eea4b161eb2df1f5108f12a4142a83322edb05a75487a3435c9a78ce53ed93bc550857d7a9fb");
+
+ private byte[] slt8a = Hex.decode("1d65491d79c864b373009be6f6f2467bac4c78fa");
+
+ private byte[] sig8a = Hex.decode("0262ac254bfa77f3c1aca22c5179f8f040422b3c5bafd40a8f21cf0fa5a667ccd5993d42dbafb409c520e25fce2b1ee1e716577f1efa17f3da28052f40f0419b23106d7845aaf01125b698e7a4dfe92d3967bb00c4d0d35ba3552ab9a8b3eef07c7fecdbc5424ac4db1e20cb37d0b2744769940ea907e17fbbca673b20522380c5");
+
+ // PSS Example 8.2
+
+ private byte[] msg8b = Hex.decode("e2f96eaf0e05e7ba326ecca0ba7fd2f7c02356f3cede9d0faabf4fcc8e60a973e5595fd9ea08");
+
+ private byte[] slt8b = Hex.decode("435c098aa9909eb2377f1248b091b68987ff1838");
+
+ private byte[] sig8b = Hex.decode("2707b9ad5115c58c94e932e8ec0a280f56339e44a1b58d4ddcff2f312e5f34dcfe39e89c6a94dcee86dbbdae5b79ba4e0819a9e7bfd9d982e7ee6c86ee68396e8b3a14c9c8f34b178eb741f9d3f121109bf5c8172fada2e768f9ea1433032c004a8aa07eb990000a48dc94c8bac8aabe2b09b1aa46c0a2aa0e12f63fbba775ba7e");
+
+ //
+ // Example 9: A 1536-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub9 = new RSAKeyParameters(false,
+ new BigInteger("e6bd692ac96645790403fdd0f5beb8b9bf92ed10007fc365046419dd06c05c5b5b2f48ecf989e4ce269109979cbb40b4a0ad24d22483d1ee315ad4ccb1534268352691c524f6dd8e6c29d224cf246973aec86c5bf6b1401a850d1b9ad1bb8cbcec47b06f0f8c7f45d3fc8f319299c5433ddbc2b3053b47ded2ecd4a4caefd614833dc8bb622f317ed076b8057fe8de3f84480ad5e83e4a61904a4f248fb397027357e1d30e463139815c6fd4fd5ac5b8172a45230ecb6318a04f1455d84e5a8b", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv9 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("e6bd692ac96645790403fdd0f5beb8b9bf92ed10007fc365046419dd06c05c5b5b2f48ecf989e4ce269109979cbb40b4a0ad24d22483d1ee315ad4ccb1534268352691c524f6dd8e6c29d224cf246973aec86c5bf6b1401a850d1b9ad1bb8cbcec47b06f0f8c7f45d3fc8f319299c5433ddbc2b3053b47ded2ecd4a4caefd614833dc8bb622f317ed076b8057fe8de3f84480ad5e83e4a61904a4f248fb397027357e1d30e463139815c6fd4fd5ac5b8172a45230ecb6318a04f1455d84e5a8b", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("6a7fd84fb85fad073b34406db74f8d61a6abc12196a961dd79565e9da6e5187bce2d980250f7359575359270d91590bb0e427c71460b55d51410b191bcf309fea131a92c8e702738fa719f1e0041f52e40e91f229f4d96a1e6f172e15596b4510a6daec26105f2bebc53316b87bdf21311666070e8dfee69d52c71a976caae79c72b68d28580dc686d9f5129d225f82b3d615513a882b3db91416b48ce08888213e37eeb9af800d81cab328ce420689903c00c7b5fd31b75503a6d419684d629", 16),
+ new BigInteger("f8eb97e98df12664eefdb761596a69ddcd0e76daece6ed4bf5a1b50ac086f7928a4d2f8726a77e515b74da41988f220b1cc87aa1fc810ce99a82f2d1ce821edced794c6941f42c7a1a0b8c4d28c75ec60b652279f6154a762aed165d47dee367", 16),
+ new BigInteger("ed4d71d0a6e24b93c2e5f6b4bbe05f5fb0afa042d204fe3378d365c2f288b6a8dad7efe45d153eef40cacc7b81ff934002d108994b94a5e4728cd9c963375ae49965bda55cbf0efed8d6553b4027f2d86208a6e6b489c176128092d629e49d3d", 16),
+ new BigInteger("2bb68bddfb0c4f56c8558bffaf892d8043037841e7fa81cfa61a38c5e39b901c8ee71122a5da2227bd6cdeeb481452c12ad3d61d5e4f776a0ab556591befe3e59e5a7fddb8345e1f2f35b9f4cee57c32414c086aec993e9353e480d9eec6289f", 16),
+ new BigInteger("4ff897709fad079746494578e70fd8546130eeab5627c49b080f05ee4ad9f3e4b7cba9d6a5dff113a41c3409336833f190816d8a6bc42e9bec56b7567d0f3c9c696db619b245d901dd856db7c8092e77e9a1cccd56ee4dba42c5fdb61aec2669", 16),
+ new BigInteger("77b9d1137b50404a982729316efafc7dfe66d34e5a182600d5f30a0a8512051c560d081d4d0a1835ec3d25a60f4e4d6aa948b2bf3dbb5b124cbbc3489255a3a948372f6978496745f943e1db4f18382ceaa505dfc65757bb3f857a58dce52156", 16));
+
+ // PSS Example 9.1
+
+ private byte[] msg9a = Hex.decode("a88e265855e9d7ca36c68795f0b31b591cd6587c71d060a0b3f7f3eaef43795922028bc2b6ad467cfc2d7f659c5385aa70ba3672cdde4cfe4970cc7904601b278872bf51321c4a972f3c95570f3445d4f57980e0f20df54846e6a52c668f1288c03f95006ea32f562d40d52af9feb32f0fa06db65b588a237b34e592d55cf979f903a642ef64d2ed542aa8c77dc1dd762f45a59303ed75e541ca271e2b60ca709e44fa0661131e8d5d4163fd8d398566ce26de8730e72f9cca737641c244159420637028df0a18079d6208ea8b4711a2c750f5");
+
+ private byte[] slt9a = Hex.decode("c0a425313df8d7564bd2434d311523d5257eed80");
+
+ private byte[] sig9a = Hex.decode("586107226c3ce013a7c8f04d1a6a2959bb4b8e205ba43a27b50f124111bc35ef589b039f5932187cb696d7d9a32c0c38300a5cdda4834b62d2eb240af33f79d13dfbf095bf599e0d9686948c1964747b67e89c9aba5cd85016236f566cc5802cb13ead51bc7ca6bef3b94dcbdbb1d570469771df0e00b1a8a06777472d2316279edae86474668d4e1efff95f1de61c6020da32ae92bbf16520fef3cf4d88f61121f24bbd9fe91b59caf1235b2a93ff81fc403addf4ebdea84934a9cdaf8e1a9e");
+
+ // PSS Example 9.2
+
+ private byte[] msg9b = Hex.decode("c8c9c6af04acda414d227ef23e0820c3732c500dc87275e95b0d095413993c2658bc1d988581ba879c2d201f14cb88ced153a01969a7bf0a7be79c84c1486bc12b3fa6c59871b6827c8ce253ca5fefa8a8c690bf326e8e37cdb96d90a82ebab69f86350e1822e8bd536a2e");
+
+ private byte[] slt9b = Hex.decode("b307c43b4850a8dac2f15f32e37839ef8c5c0e91");
+
+ private byte[] sig9b = Hex.decode("80b6d643255209f0a456763897ac9ed259d459b49c2887e5882ecb4434cfd66dd7e1699375381e51cd7f554f2c271704b399d42b4be2540a0eca61951f55267f7c2878c122842dadb28b01bd5f8c025f7e228418a673c03d6bc0c736d0a29546bd67f786d9d692ccea778d71d98c2063b7a71092187a4d35af108111d83e83eae46c46aa34277e06044589903788f1d5e7cee25fb485e92949118814d6f2c3ee361489016f327fb5bc517eb50470bffa1afa5f4ce9aa0ce5b8ee19bf5501b958");
+
+
+ public String getName()
+ {
+ return "PSSBlindTest";
+ }
+
+ private void testSig(
+ int id,
+ RSAKeyParameters pub,
+ RSAKeyParameters prv,
+ byte[] slt,
+ byte[] msg,
+ byte[] sig)
+ throws Exception
+ {
+ RSABlindingFactorGenerator blindFactorGen = new RSABlindingFactorGenerator();
+ RSABlindingEngine blindingEngine = new RSABlindingEngine();
+ PSSSigner blindSigner = new PSSSigner(blindingEngine, new SHA1Digest(), 20);
+ PSSSigner signer = new PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+
+ blindFactorGen.init(pub);
+
+ BigInteger blindFactor = blindFactorGen.generateBlindingFactor();
+ RSABlindingParameters params = new RSABlindingParameters(pub, blindFactor);
+
+ // generate a blind signature
+ blindSigner.init(true, new ParametersWithRandom(params, new FixedRandom(slt)));
+
+ blindSigner.update(msg, 0, msg.length);
+
+ byte[] blindedData = blindSigner.generateSignature();
+
+ RSAEngine signerEngine = new RSAEngine();
+
+ signerEngine.init(true, prv);
+
+ byte[] blindedSig = signerEngine.processBlock(blindedData, 0, blindedData.length);
+
+ // unblind the signature
+ blindingEngine.init(false, params);
+
+ byte[] s = blindingEngine.processBlock(blindedSig, 0, blindedSig.length);
+
+ //signature verification
+ if (!areEqual(s, sig))
+ {
+ fail("test " + id + " failed generation");
+ }
+
+ //verify signature with PSSSigner
+ signer.init(false, pub);
+ signer.update(msg, 0, msg.length);
+
+ if (!signer.verifySignature(s))
+ {
+ fail("test " + id + " failed PSSSigner verification");
+ }
+ }
+
+ private boolean isProcessingOkay(
+ RSAKeyParameters pub,
+ RSAKeyParameters prv,
+ byte[] data,
+ SecureRandom random)
+ throws Exception
+ {
+ RSABlindingFactorGenerator blindFactorGen = new RSABlindingFactorGenerator();
+ RSABlindingEngine blindingEngine = new RSABlindingEngine();
+ PSSSigner blindSigner = new PSSSigner(blindingEngine, new SHA1Digest(), 20);
+ PSSSigner pssEng = new PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+
+ random.nextBytes(data);
+
+ blindFactorGen.init(pub);
+
+ BigInteger blindFactor = blindFactorGen.generateBlindingFactor();
+ RSABlindingParameters params = new RSABlindingParameters(pub, blindFactor);
+
+ // generate a blind signature
+ blindSigner.init(true, new ParametersWithRandom(params, random));
+
+ blindSigner.update(data, 0, data.length);
+
+ byte[] blindedData = blindSigner.generateSignature();
+
+ RSAEngine signerEngine = new RSAEngine();
+
+ signerEngine.init(true, prv);
+
+ byte[] blindedSig = signerEngine.processBlock(blindedData, 0, blindedData.length);
+
+ // unblind the signature
+ blindingEngine.init(false, params);
+
+ byte[] s = blindingEngine.processBlock(blindedSig, 0, blindedSig.length);
+
+ //verify signature with PSSSigner
+ pssEng.init(false, pub);
+ pssEng.update(data, 0, data.length);
+
+ return pssEng.verifySignature(s);
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testSig(1, pub1, prv1, slt1a, msg1a, sig1a);
+ testSig(2, pub1, prv1, slt1b, msg1b, sig1b);
+ testSig(3, pub2, prv2, slt2a, msg2a, sig2a);
+ testSig(4, pub2, prv2, slt2b, msg2b, sig2b);
+ testSig(5, pub4, prv4, slt4a, msg4a, sig4a);
+ testSig(6, pub4, prv4, slt4b, msg4b, sig4b);
+ testSig(7, pub8, prv8, slt8a, msg8a, sig8a);
+ testSig(8, pub8, prv8, slt8b, msg8b, sig8b);
+ testSig(9, pub9, prv9, slt9a, msg9a, sig9a);
+ testSig(10, pub9, prv9, slt9b, msg9b, sig9b);
+
+ //
+ // loop test
+ //
+ int failed = 0;
+ byte[] data = new byte[DATA_LENGTH];
+
+ SecureRandom random = new SecureRandom();
+
+
+ RSAKeyParameters[] kprv ={prv1, prv2, prv4, prv8, prv9};
+ RSAKeyParameters[] kpub ={pub1, pub2, pub4, pub8, pub9};
+
+ int i = 0;
+ for (int j = 0; j < NUM_TESTS; j++, i++)
+ {
+ if (i == kprv.length)
+ {
+ i = 0;
+ }
+
+ if (!isProcessingOkay(kpub[i], kprv[i], data, random))
+ {
+ failed++;
+ }
+ }
+
+ if (failed != 0)
+ {
+ fail("loop test failed - failures: " + failed);
+ }
+
+ //
+ // key generation test
+ //
+ RSAKeyPairGenerator pGen = new RSAKeyPairGenerator();
+ RSAKeyGenerationParameters genParam = new RSAKeyGenerationParameters(
+ BigInteger.valueOf(0x11), new SecureRandom(), 1024, 25);
+
+ pGen.init(genParam);
+ failed = 0;
+
+ for (int k = 0; k < NUM_TESTS_WITH_KEY_GENERATION; k++)
+ {
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ for (int j = 0; j < NUM_TESTS; j++)
+ {
+ if (!isProcessingOkay((RSAKeyParameters)pair.getPublic(), (RSAKeyParameters)pair.getPrivate(), data, random))
+ {
+ failed++;
+ }
+ }
+
+ }
+
+ if (failed != 0)
+ {
+ fail("loop test with key generation failed - failures: " + failed);
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PSSBlindTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/PSSTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/PSSTest.java
new file mode 100644
index 00000000..a718f8cd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/PSSTest.java
@@ -0,0 +1,332 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.crypto.signers.PSSSigner;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/*
+ * RSA PSS test vectors for PKCS#1 V2.1
+ */
+public class PSSTest
+ extends SimpleTest
+{
+ private final int DATA_LENGTH = 1000;
+ private final int NUM_TESTS = 500;
+
+ private class FixedRandom
+ extends SecureRandom
+ {
+ byte[] vals;
+
+ FixedRandom(
+ byte[] vals)
+ {
+ this.vals = vals;
+ }
+
+ public void nextBytes(
+ byte[] bytes)
+ {
+ System.arraycopy(vals, 0, bytes, 0, vals.length);
+ }
+ }
+
+ //
+ // Example 1: A 1024-bit RSA keypair
+ //
+ private RSAKeyParameters pub1 = new RSAKeyParameters(false,
+ new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
+ new BigInteger("010001",16));
+
+ private RSAKeyParameters prv1 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16),
+ new BigInteger("010001",16),
+ new BigInteger("33a5042a90b27d4f5451ca9bbbd0b44771a101af884340aef9885f2a4bbe92e894a724ac3c568c8f97853ad07c0266c8c6a3ca0929f1e8f11231884429fc4d9ae55fee896a10ce707c3ed7e734e44727a39574501a532683109c2abacaba283c31b4bd2f53c3ee37e352cee34f9e503bd80c0622ad79c6dcee883547c6a3b325",16),
+ new BigInteger("e7e8942720a877517273a356053ea2a1bc0c94aa72d55c6e86296b2dfc967948c0a72cbccca7eacb35706e09a1df55a1535bd9b3cc34160b3b6dcd3eda8e6443",16),
+ new BigInteger("b69dca1cf7d4d7ec81e75b90fcca874abcde123fd2700180aa90479b6e48de8d67ed24f9f19d85ba275874f542cd20dc723e6963364a1f9425452b269a6799fd",16),
+ new BigInteger("28fa13938655be1f8a159cbaca5a72ea190c30089e19cd274a556f36c4f6e19f554b34c077790427bbdd8dd3ede2448328f385d81b30e8e43b2fffa027861979",16),
+ new BigInteger("1a8b38f398fa712049898d7fb79ee0a77668791299cdfa09efc0e507acb21ed74301ef5bfd48be455eaeb6e1678255827580a8e4e8e14151d1510a82a3f2e729",16),
+ new BigInteger("27156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24a79f4d",16));
+
+ // PSSExample1.1
+
+ private byte[] msg1a = Hex.decode("cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73318b750a0167d0");
+
+ private byte[] slt1a = Hex.decode("dee959c7e06411361420ff80185ed57f3e6776af");
+
+ private byte[] sig1a = Hex.decode("9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f382a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd500f1ff2b994d8a4e30cbb33c");
+
+ // PSSExample1.2
+
+ private byte[] msg1b = Hex.decode("851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e");
+
+ private byte[] slt1b = Hex.decode("ef2869fa40c346cb183dab3d7bffc98fd56df42d");
+
+ private byte[] sig1b = Hex.decode("3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c729984f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193be5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98c22c4d4d47724fdb5669e843");
+
+ //
+ // Example 2: A 1025-bit RSA keypair
+ //
+
+ private RSAKeyParameters pub2 = new RSAKeyParameters(false,
+ new BigInteger("01d40c1bcf97a68ae7cdbd8a7bf3e34fa19dcca4ef75a47454375f94514d88fed006fb829f8419ff87d6315da68a1ff3a0938e9abb3464011c303ad99199cf0c7c7a8b477dce829e8844f625b115e5e9c4a59cf8f8113b6834336a2fd2689b472cbb5e5cabe674350c59b6c17e176874fb42f8fc3d176a017edc61fd326c4b33c9", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv2 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("01d40c1bcf97a68ae7cdbd8a7bf3e34fa19dcca4ef75a47454375f94514d88fed006fb829f8419ff87d6315da68a1ff3a0938e9abb3464011c303ad99199cf0c7c7a8b477dce829e8844f625b115e5e9c4a59cf8f8113b6834336a2fd2689b472cbb5e5cabe674350c59b6c17e176874fb42f8fc3d176a017edc61fd326c4b33c9", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("027d147e4673057377fd1ea201565772176a7dc38358d376045685a2e787c23c15576bc16b9f444402d6bfc5d98a3e88ea13ef67c353eca0c0ddba9255bd7b8bb50a644afdfd1dd51695b252d22e7318d1b6687a1c10ff75545f3db0fe602d5f2b7f294e3601eab7b9d1cecd767f64692e3e536ca2846cb0c2dd486a39fa75b1", 16),
+ new BigInteger("016601e926a0f8c9e26ecab769ea65a5e7c52cc9e080ef519457c644da6891c5a104d3ea7955929a22e7c68a7af9fcad777c3ccc2b9e3d3650bce404399b7e59d1", 16),
+ new BigInteger("014eafa1d4d0184da7e31f877d1281ddda625664869e8379e67ad3b75eae74a580e9827abd6eb7a002cb5411f5266797768fb8e95ae40e3e8a01f35ff89e56c079", 16),
+ new BigInteger("e247cce504939b8f0a36090de200938755e2444b29539a7da7a902f6056835c0db7b52559497cfe2c61a8086d0213c472c78851800b171f6401de2e9c2756f31", 16),
+ new BigInteger("b12fba757855e586e46f64c38a70c68b3f548d93d787b399999d4c8f0bbd2581c21e19ed0018a6d5d3df86424b3abcad40199d31495b61309f27c1bf55d487c1", 16),
+ new BigInteger("564b1e1fa003bda91e89090425aac05b91da9ee25061e7628d5f51304a84992fdc33762bd378a59f030a334d532bd0dae8f298ea9ed844636ad5fb8cbdc03cad", 16));
+
+ // PSS Example 2.1
+
+ private byte[] msg2a = Hex.decode("daba032066263faedb659848115278a52c44faa3a76f37515ed336321072c40a9d9b53bc05014078adf520875146aae70ff060226dcb7b1f1fc27e9360");
+ private byte[] slt2a = Hex.decode("57bf160bcb02bb1dc7280cf0458530b7d2832ff7");
+ private byte[] sig2a = Hex.decode("014c5ba5338328ccc6e7a90bf1c0ab3fd606ff4796d3c12e4b639ed9136a5fec6c16d8884bdd99cfdc521456b0742b736868cf90de099adb8d5ffd1deff39ba4007ab746cefdb22d7df0e225f54627dc65466131721b90af445363a8358b9f607642f78fab0ab0f43b7168d64bae70d8827848d8ef1e421c5754ddf42c2589b5b3");
+
+ // PSS Example 2.2
+
+ private byte[] msg2b = Hex.decode("e4f8601a8a6da1be34447c0959c058570c3668cfd51dd5f9ccd6ad4411fe8213486d78a6c49f93efc2ca2288cebc2b9b60bd04b1e220d86e3d4848d709d032d1e8c6a070c6af9a499fcf95354b14ba6127c739de1bb0fd16431e46938aec0cf8ad9eb72e832a7035de9b7807bdc0ed8b68eb0f5ac2216be40ce920c0db0eddd3860ed788efaccaca502d8f2bd6d1a7c1f41ff46f1681c8f1f818e9c4f6d91a0c7803ccc63d76a6544d843e084e363b8acc55aa531733edb5dee5b5196e9f03e8b731b3776428d9e457fe3fbcb3db7274442d785890e9cb0854b6444dace791d7273de1889719338a77fe");
+ private byte[] slt2b = Hex.decode("7f6dd359e604e60870e898e47b19bf2e5a7b2a90");
+ private byte[] sig2b = Hex.decode("010991656cca182b7f29d2dbc007e7ae0fec158eb6759cb9c45c5ff87c7635dd46d150882f4de1e9ae65e7f7d9018f6836954a47c0a81a8a6b6f83f2944d6081b1aa7c759b254b2c34b691da67cc0226e20b2f18b42212761dcd4b908a62b371b5918c5742af4b537e296917674fb914194761621cc19a41f6fb953fbcbb649dea");
+
+ //
+ // Example 4: A 1027-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub4 = new RSAKeyParameters(false,
+ new BigInteger("054adb7886447efe6f57e0368f06cf52b0a3370760d161cef126b91be7f89c421b62a6ec1da3c311d75ed50e0ab5fff3fd338acc3aa8a4e77ee26369acb81ba900fa83f5300cf9bb6c53ad1dc8a178b815db4235a9a9da0c06de4e615ea1277ce559e9c108de58c14a81aa77f5a6f8d1335494498848c8b95940740be7bf7c3705", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv4 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("054adb7886447efe6f57e0368f06cf52b0a3370760d161cef126b91be7f89c421b62a6ec1da3c311d75ed50e0ab5fff3fd338acc3aa8a4e77ee26369acb81ba900fa83f5300cf9bb6c53ad1dc8a178b815db4235a9a9da0c06de4e615ea1277ce559e9c108de58c14a81aa77f5a6f8d1335494498848c8b95940740be7bf7c3705", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("fa041f8cd9697ceed38ec8caa275523b4dd72b09a301d3541d72f5d31c05cbce2d6983b36183af10690bd46c46131e35789431a556771dd0049b57461bf060c1f68472e8a67c25f357e5b6b4738fa541a730346b4a07649a2dfa806a69c975b6aba64678acc7f5913e89c622f2d8abb1e3e32554e39df94ba60c002e387d9011", 16),
+ new BigInteger("029232336d2838945dba9dd7723f4e624a05f7375b927a87abe6a893a1658fd49f47f6c7b0fa596c65fa68a23f0ab432962d18d4343bd6fd671a5ea8d148413995", 16),
+ new BigInteger("020ef5efe7c5394aed2272f7e81a74f4c02d145894cb1b3cab23a9a0710a2afc7e3329acbb743d01f680c4d02afb4c8fde7e20930811bb2b995788b5e872c20bb1", 16),
+ new BigInteger("026e7e28010ecf2412d9523ad704647fb4fe9b66b1a681581b0e15553a89b1542828898f27243ebab45ff5e1acb9d4df1b051fbc62824dbc6f6c93261a78b9a759", 16),
+ new BigInteger("012ddcc86ef655998c39ddae11718669e5e46cf1495b07e13b1014cd69b3af68304ad2a6b64321e78bf3bbca9bb494e91d451717e2d97564c6549465d0205cf421", 16),
+ new BigInteger("010600c4c21847459fe576703e2ebecae8a5094ee63f536bf4ac68d3c13e5e4f12ac5cc10ab6a2d05a199214d1824747d551909636b774c22cac0b837599abcc75", 16));
+
+ // PSS Example 4.1
+
+ private byte[] msg4a = Hex.decode("9fb03b827c8217d9");
+
+ private byte[] slt4a = Hex.decode("ed7c98c95f30974fbe4fbddcf0f28d6021c0e91d");
+
+ private byte[] sig4a = Hex.decode("0323d5b7bf20ba4539289ae452ae4297080feff4518423ff4811a817837e7d82f1836cdfab54514ff0887bddeebf40bf99b047abc3ecfa6a37a3ef00f4a0c4a88aae0904b745c846c4107e8797723e8ac810d9e3d95dfa30ff4966f4d75d13768d20857f2b1406f264cfe75e27d7652f4b5ed3575f28a702f8c4ed9cf9b2d44948");
+
+ // PSS Example 4.2
+
+ private byte[] msg4b = Hex.decode("0ca2ad77797ece86de5bf768750ddb5ed6a3116ad99bbd17edf7f782f0db1cd05b0f677468c5ea420dc116b10e80d110de2b0461ea14a38be68620392e7e893cb4ea9393fb886c20ff790642305bf302003892e54df9f667509dc53920df583f50a3dd61abb6fab75d600377e383e6aca6710eeea27156e06752c94ce25ae99fcbf8592dbe2d7e27453cb44de07100ebb1a2a19811a478adbeab270f94e8fe369d90b3ca612f9f");
+
+ private byte[] slt4b = Hex.decode("22d71d54363a4217aa55113f059b3384e3e57e44");
+
+ private byte[] sig4b = Hex.decode("049d0185845a264d28feb1e69edaec090609e8e46d93abb38371ce51f4aa65a599bdaaa81d24fba66a08a116cb644f3f1e653d95c89db8bbd5daac2709c8984000178410a7c6aa8667ddc38c741f710ec8665aa9052be929d4e3b16782c1662114c5414bb0353455c392fc28f3db59054b5f365c49e1d156f876ee10cb4fd70598");
+
+
+ //
+ // Example 8: A 1031-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub8 = new RSAKeyParameters(false,
+ new BigInteger("495370a1fb18543c16d3631e3163255df62be6eee890d5f25509e4f778a8ea6fbbbcdf85dff64e0d972003ab3681fbba6dd41fd541829b2e582de9f2a4a4e0a2d0900bef4753db3cee0ee06c7dfae8b1d53b5953218f9cceea695b08668edeaadced9463b1d790d5ebf27e9115b46cad4d9a2b8efab0561b0810344739ada0733f", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv8 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("495370a1fb18543c16d3631e3163255df62be6eee890d5f25509e4f778a8ea6fbbbcdf85dff64e0d972003ab3681fbba6dd41fd541829b2e582de9f2a4a4e0a2d0900bef4753db3cee0ee06c7dfae8b1d53b5953218f9cceea695b08668edeaadced9463b1d790d5ebf27e9115b46cad4d9a2b8efab0561b0810344739ada0733f", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("6c66ffe98980c38fcdeab5159898836165f4b4b817c4f6a8d486ee4ea9130fe9b9092bd136d184f95f504a607eac565846d2fdd6597a8967c7396ef95a6eeebb4578a643966dca4d8ee3de842de63279c618159c1ab54a89437b6a6120e4930afb52a4ba6ced8a4947ac64b30a3497cbe701c2d6266d517219ad0ec6d347dbe9", 16),
+ new BigInteger("08dad7f11363faa623d5d6d5e8a319328d82190d7127d2846c439b0ab72619b0a43a95320e4ec34fc3a9cea876422305bd76c5ba7be9e2f410c8060645a1d29edb", 16),
+ new BigInteger("0847e732376fc7900f898ea82eb2b0fc418565fdae62f7d9ec4ce2217b97990dd272db157f99f63c0dcbb9fbacdbd4c4dadb6df67756358ca4174825b48f49706d", 16),
+ new BigInteger("05c2a83c124b3621a2aa57ea2c3efe035eff4560f33ddebb7adab81fce69a0c8c2edc16520dda83d59a23be867963ac65f2cc710bbcfb96ee103deb771d105fd85", 16),
+ new BigInteger("04cae8aa0d9faa165c87b682ec140b8ed3b50b24594b7a3b2c220b3669bb819f984f55310a1ae7823651d4a02e99447972595139363434e5e30a7e7d241551e1b9", 16),
+ new BigInteger("07d3e47bf686600b11ac283ce88dbb3f6051e8efd04680e44c171ef531b80b2b7c39fc766320e2cf15d8d99820e96ff30dc69691839c4b40d7b06e45307dc91f3f", 16));
+
+ // PSS Example 8.1
+
+ private byte[] msg8a = Hex.decode("81332f4be62948415ea1d899792eeacf6c6e1db1da8be13b5cea41db2fed467092e1ff398914c714259775f595f8547f735692a575e6923af78f22c6997ddb90fb6f72d7bb0dd5744a31decd3dc3685849836ed34aec596304ad11843c4f88489f209735f5fb7fdaf7cec8addc5818168f880acbf490d51005b7a8e84e43e54287977571dd99eea4b161eb2df1f5108f12a4142a83322edb05a75487a3435c9a78ce53ed93bc550857d7a9fb");
+
+ private byte[] slt8a = Hex.decode("1d65491d79c864b373009be6f6f2467bac4c78fa");
+
+ private byte[] sig8a = Hex.decode("0262ac254bfa77f3c1aca22c5179f8f040422b3c5bafd40a8f21cf0fa5a667ccd5993d42dbafb409c520e25fce2b1ee1e716577f1efa17f3da28052f40f0419b23106d7845aaf01125b698e7a4dfe92d3967bb00c4d0d35ba3552ab9a8b3eef07c7fecdbc5424ac4db1e20cb37d0b2744769940ea907e17fbbca673b20522380c5");
+
+ // PSS Example 8.2
+
+ private byte[] msg8b = Hex.decode("e2f96eaf0e05e7ba326ecca0ba7fd2f7c02356f3cede9d0faabf4fcc8e60a973e5595fd9ea08");
+
+ private byte[] slt8b = Hex.decode("435c098aa9909eb2377f1248b091b68987ff1838");
+
+ private byte[] sig8b = Hex.decode("2707b9ad5115c58c94e932e8ec0a280f56339e44a1b58d4ddcff2f312e5f34dcfe39e89c6a94dcee86dbbdae5b79ba4e0819a9e7bfd9d982e7ee6c86ee68396e8b3a14c9c8f34b178eb741f9d3f121109bf5c8172fada2e768f9ea1433032c004a8aa07eb990000a48dc94c8bac8aabe2b09b1aa46c0a2aa0e12f63fbba775ba7e");
+
+ //
+ // Example 9: A 1536-bit RSA key pair
+ //
+
+ private RSAKeyParameters pub9 = new RSAKeyParameters(false,
+ new BigInteger("e6bd692ac96645790403fdd0f5beb8b9bf92ed10007fc365046419dd06c05c5b5b2f48ecf989e4ce269109979cbb40b4a0ad24d22483d1ee315ad4ccb1534268352691c524f6dd8e6c29d224cf246973aec86c5bf6b1401a850d1b9ad1bb8cbcec47b06f0f8c7f45d3fc8f319299c5433ddbc2b3053b47ded2ecd4a4caefd614833dc8bb622f317ed076b8057fe8de3f84480ad5e83e4a61904a4f248fb397027357e1d30e463139815c6fd4fd5ac5b8172a45230ecb6318a04f1455d84e5a8b", 16),
+ new BigInteger("010001", 16));
+
+ private RSAKeyParameters prv9 = new RSAPrivateCrtKeyParameters(
+ new BigInteger("e6bd692ac96645790403fdd0f5beb8b9bf92ed10007fc365046419dd06c05c5b5b2f48ecf989e4ce269109979cbb40b4a0ad24d22483d1ee315ad4ccb1534268352691c524f6dd8e6c29d224cf246973aec86c5bf6b1401a850d1b9ad1bb8cbcec47b06f0f8c7f45d3fc8f319299c5433ddbc2b3053b47ded2ecd4a4caefd614833dc8bb622f317ed076b8057fe8de3f84480ad5e83e4a61904a4f248fb397027357e1d30e463139815c6fd4fd5ac5b8172a45230ecb6318a04f1455d84e5a8b", 16),
+ new BigInteger("010001", 16),
+ new BigInteger("6a7fd84fb85fad073b34406db74f8d61a6abc12196a961dd79565e9da6e5187bce2d980250f7359575359270d91590bb0e427c71460b55d51410b191bcf309fea131a92c8e702738fa719f1e0041f52e40e91f229f4d96a1e6f172e15596b4510a6daec26105f2bebc53316b87bdf21311666070e8dfee69d52c71a976caae79c72b68d28580dc686d9f5129d225f82b3d615513a882b3db91416b48ce08888213e37eeb9af800d81cab328ce420689903c00c7b5fd31b75503a6d419684d629", 16),
+ new BigInteger("f8eb97e98df12664eefdb761596a69ddcd0e76daece6ed4bf5a1b50ac086f7928a4d2f8726a77e515b74da41988f220b1cc87aa1fc810ce99a82f2d1ce821edced794c6941f42c7a1a0b8c4d28c75ec60b652279f6154a762aed165d47dee367", 16),
+ new BigInteger("ed4d71d0a6e24b93c2e5f6b4bbe05f5fb0afa042d204fe3378d365c2f288b6a8dad7efe45d153eef40cacc7b81ff934002d108994b94a5e4728cd9c963375ae49965bda55cbf0efed8d6553b4027f2d86208a6e6b489c176128092d629e49d3d", 16),
+ new BigInteger("2bb68bddfb0c4f56c8558bffaf892d8043037841e7fa81cfa61a38c5e39b901c8ee71122a5da2227bd6cdeeb481452c12ad3d61d5e4f776a0ab556591befe3e59e5a7fddb8345e1f2f35b9f4cee57c32414c086aec993e9353e480d9eec6289f", 16),
+ new BigInteger("4ff897709fad079746494578e70fd8546130eeab5627c49b080f05ee4ad9f3e4b7cba9d6a5dff113a41c3409336833f190816d8a6bc42e9bec56b7567d0f3c9c696db619b245d901dd856db7c8092e77e9a1cccd56ee4dba42c5fdb61aec2669", 16),
+ new BigInteger("77b9d1137b50404a982729316efafc7dfe66d34e5a182600d5f30a0a8512051c560d081d4d0a1835ec3d25a60f4e4d6aa948b2bf3dbb5b124cbbc3489255a3a948372f6978496745f943e1db4f18382ceaa505dfc65757bb3f857a58dce52156", 16));
+
+ // PSS Example 9.1
+
+ private byte[] msg9a = Hex.decode("a88e265855e9d7ca36c68795f0b31b591cd6587c71d060a0b3f7f3eaef43795922028bc2b6ad467cfc2d7f659c5385aa70ba3672cdde4cfe4970cc7904601b278872bf51321c4a972f3c95570f3445d4f57980e0f20df54846e6a52c668f1288c03f95006ea32f562d40d52af9feb32f0fa06db65b588a237b34e592d55cf979f903a642ef64d2ed542aa8c77dc1dd762f45a59303ed75e541ca271e2b60ca709e44fa0661131e8d5d4163fd8d398566ce26de8730e72f9cca737641c244159420637028df0a18079d6208ea8b4711a2c750f5");
+
+ private byte[] slt9a = Hex.decode("c0a425313df8d7564bd2434d311523d5257eed80");
+
+ private byte[] sig9a = Hex.decode("586107226c3ce013a7c8f04d1a6a2959bb4b8e205ba43a27b50f124111bc35ef589b039f5932187cb696d7d9a32c0c38300a5cdda4834b62d2eb240af33f79d13dfbf095bf599e0d9686948c1964747b67e89c9aba5cd85016236f566cc5802cb13ead51bc7ca6bef3b94dcbdbb1d570469771df0e00b1a8a06777472d2316279edae86474668d4e1efff95f1de61c6020da32ae92bbf16520fef3cf4d88f61121f24bbd9fe91b59caf1235b2a93ff81fc403addf4ebdea84934a9cdaf8e1a9e");
+
+ // PSS Example 9.2
+
+ private byte[] msg9b = Hex.decode("c8c9c6af04acda414d227ef23e0820c3732c500dc87275e95b0d095413993c2658bc1d988581ba879c2d201f14cb88ced153a01969a7bf0a7be79c84c1486bc12b3fa6c59871b6827c8ce253ca5fefa8a8c690bf326e8e37cdb96d90a82ebab69f86350e1822e8bd536a2e");
+
+ private byte[] slt9b = Hex.decode("b307c43b4850a8dac2f15f32e37839ef8c5c0e91");
+
+ private byte[] sig9b = Hex.decode("80b6d643255209f0a456763897ac9ed259d459b49c2887e5882ecb4434cfd66dd7e1699375381e51cd7f554f2c271704b399d42b4be2540a0eca61951f55267f7c2878c122842dadb28b01bd5f8c025f7e228418a673c03d6bc0c736d0a29546bd67f786d9d692ccea778d71d98c2063b7a71092187a4d35af108111d83e83eae46c46aa34277e06044589903788f1d5e7cee25fb485e92949118814d6f2c3ee361489016f327fb5bc517eb50470bffa1afa5f4ce9aa0ce5b8ee19bf5501b958");
+
+
+ public String getName()
+ {
+ return "PSSTest";
+ }
+
+ private void testSig(
+ int id,
+ RSAKeyParameters pub,
+ RSAKeyParameters prv,
+ byte[] slt,
+ byte[] msg,
+ byte[] sig)
+ throws Exception
+ {
+ PSSSigner eng = new PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+
+ eng.init(true, new ParametersWithRandom(prv, new FixedRandom(slt)));
+
+ eng.update(msg, 0, msg.length);
+
+ byte[] s = eng.generateSignature();
+
+ if (!areEqual(s, sig))
+ {
+ fail("test " + id + " failed generation");
+ }
+
+ eng.init(false, pub);
+
+ eng.update(msg, 0, msg.length);
+
+ if (!eng.verifySignature(s))
+ {
+ fail("test " + id + " failed verification");
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testSig(1, pub1, prv1, slt1a, msg1a, sig1a);
+ testSig(2, pub1, prv1, slt1b, msg1b, sig1b);
+ testSig(3, pub2, prv2, slt2a, msg2a, sig2a);
+ testSig(4, pub2, prv2, slt2b, msg2b, sig2b);
+ testSig(5, pub4, prv4, slt4a, msg4a, sig4a);
+ testSig(6, pub4, prv4, slt4b, msg4b, sig4b);
+ testSig(7, pub8, prv8, slt8a, msg8a, sig8a);
+ testSig(8, pub8, prv8, slt8b, msg8b, sig8b);
+ testSig(9, pub9, prv9, slt9a, msg9a, sig9a);
+ testSig(10, pub9, prv9, slt9b, msg9b, sig9b);
+
+ //
+ // loop test - sha-1 only
+ //
+ PSSSigner eng = new PSSSigner(new RSAEngine(), new SHA1Digest(), 20);
+ int failed = 0;
+ byte[] data = new byte[DATA_LENGTH];
+
+ SecureRandom random = new SecureRandom();
+ random.nextBytes(data);
+
+ for (int j = 0; j < NUM_TESTS; j++)
+ {
+ eng.init(true, new ParametersWithRandom(prv8, random));
+
+ eng.update(data, 0, data.length);
+
+ byte[] s = eng.generateSignature();
+
+ eng.init(false, pub8);
+
+ eng.update(data, 0, data.length);
+
+ if (!eng.verifySignature(s))
+ {
+ failed++;
+ }
+ }
+
+ if (failed != 0)
+ {
+ fail("loop test failed - failures: " + failed);
+ }
+
+ //
+ // loop test - sha-256 and sha-1
+ //
+ eng = new PSSSigner(new RSAEngine(), new SHA256Digest(), new SHA1Digest(), 20);
+ failed = 0;
+ data = new byte[DATA_LENGTH];
+
+ random.nextBytes(data);
+
+ for (int j = 0; j < NUM_TESTS; j++)
+ {
+ eng.init(true, new ParametersWithRandom(prv8, random));
+
+ eng.update(data, 0, data.length);
+
+ byte[] s = eng.generateSignature();
+
+ eng.init(false, pub8);
+
+ eng.update(data, 0, data.length);
+
+ if (!eng.verifySignature(s))
+ {
+ failed++;
+ }
+ }
+
+ if (failed != 0)
+ {
+ fail("loop test failed - failures: " + failed);
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PSSTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/PaddingTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/PaddingTest.java
new file mode 100644
index 00000000..c963b26e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/PaddingTest.java
@@ -0,0 +1,200 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.paddings.BlockCipherPadding;
+import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
+import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+import org.bouncycastle.crypto.paddings.PKCS7Padding;
+import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
+import org.bouncycastle.crypto.paddings.TBCPadding;
+import org.bouncycastle.crypto.paddings.X923Padding;
+import org.bouncycastle.crypto.paddings.ZeroBytePadding;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * General Padding tests.
+ */
+public class PaddingTest
+ extends SimpleTest
+{
+ public PaddingTest()
+ {
+ }
+
+ private void blockCheck(
+ PaddedBufferedBlockCipher cipher,
+ BlockCipherPadding padding,
+ KeyParameter key,
+ byte[] data)
+ {
+ byte[] out = new byte[data.length + 8];
+ byte[] dec = new byte[data.length];
+
+ try
+ {
+ cipher.init(true, key);
+
+ int len = cipher.processBytes(data, 0, data.length, out, 0);
+
+ len += cipher.doFinal(out, len);
+
+ cipher.init(false, key);
+
+ int decLen = cipher.processBytes(out, 0, len, dec, 0);
+
+ decLen += cipher.doFinal(dec, decLen);
+
+ if (!areEqual(data, dec))
+ {
+ fail("failed to decrypt - i = " + data.length + ", padding = " + padding.getPaddingName());
+ }
+ }
+ catch (Exception e)
+ {
+ fail("Exception - " + e.toString(), e);
+ }
+ }
+
+ public void testPadding(
+ BlockCipherPadding padding,
+ SecureRandom rand,
+ byte[] ffVector,
+ byte[] ZeroVector)
+ {
+ PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new DESEngine(), padding);
+ KeyParameter key = new KeyParameter(Hex.decode("0011223344556677"));
+
+ //
+ // ff test
+ //
+ byte[] data = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0 };
+
+ if (ffVector != null)
+ {
+ padding.addPadding(data, 3);
+
+ if (!areEqual(data, ffVector))
+ {
+ fail("failed ff test for " + padding.getPaddingName());
+ }
+ }
+
+ //
+ // zero test
+ //
+ if (ZeroVector != null)
+ {
+ data = new byte[8];
+ padding.addPadding(data, 4);
+
+ if (!areEqual(data, ZeroVector))
+ {
+ fail("failed zero test for " + padding.getPaddingName());
+ }
+ }
+
+ for (int i = 1; i != 200; i++)
+ {
+ data = new byte[i];
+
+ rand.nextBytes(data);
+
+ blockCheck(cipher, padding, key, data);
+ }
+ }
+
+ private void testOutputSizes()
+ {
+ PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(new DESEngine(), new PKCS7Padding());
+ KeyParameter key = new KeyParameter(Hex.decode("0011223344556677"));
+
+ for (int i = 0; i < bc.getBlockSize() * 2; i++)
+ {
+ bc.init(true, key);
+ if (bc.getUpdateOutputSize(i) < 0)
+ {
+ fail("Padded cipher encrypt negative update output size for input size " + i);
+ }
+ if (bc.getOutputSize(i) < 0)
+ {
+ fail("Padded cipher encrypt negative output size for input size " + i);
+ }
+
+ bc.init(false, key);
+ if (bc.getUpdateOutputSize(i) < 0)
+ {
+ fail("Padded cipher decrypt negative update output size for input size " + i);
+ }
+ if (bc.getOutputSize(i) < 0)
+ {
+ fail("Padded cipher decrypt negative output size for input size " + i);
+ }
+
+ }
+ }
+
+ public void performTest()
+ {
+ SecureRandom rand = new SecureRandom(new byte[20]);
+
+ rand.setSeed(System.currentTimeMillis());
+
+ testPadding(new PKCS7Padding(), rand,
+ Hex.decode("ffffff0505050505"),
+ Hex.decode("0000000004040404"));
+
+ PKCS7Padding padder = new PKCS7Padding();
+ try
+ {
+ padder.padCount(new byte[8]);
+
+ fail("invalid padding not detected");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!"pad block corrupted".equals(e.getMessage()))
+ {
+ fail("wrong exception for corrupt padding: " + e);
+ }
+ }
+
+ testPadding(new ISO10126d2Padding(), rand,
+ null,
+ null);
+
+ testPadding(new X923Padding(), rand,
+ null,
+ null);
+
+ testPadding(new TBCPadding(), rand,
+ Hex.decode("ffffff0000000000"),
+ Hex.decode("00000000ffffffff"));
+
+ testPadding(new ZeroBytePadding(), rand,
+ Hex.decode("ffffff0000000000"),
+ null);
+
+ testPadding(new ISO7816d4Padding(), rand,
+ Hex.decode("ffffff8000000000"),
+ Hex.decode("0000000080000000"));
+
+ testOutputSizes();
+
+ }
+
+ public String getName()
+ {
+ return "PaddingTest";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new PaddingTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Poly1305Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Poly1305Test.java
new file mode 100644
index 00000000..08ee8e10
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Poly1305Test.java
@@ -0,0 +1,388 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
+import org.bouncycastle.crypto.macs.Poly1305;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/*
+ */
+public class Poly1305Test
+ extends SimpleTest
+{
+ private static final int MAXLEN = 1000;
+
+ private static class KeyEngine
+ implements BlockCipher
+ {
+
+ private byte[] key;
+ private final int blockSize;
+
+ public KeyEngine(int blockSize)
+ {
+ this.blockSize = blockSize;
+ }
+
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException
+ {
+ if (params instanceof KeyParameter)
+ {
+ this.key = ((KeyParameter)params).getKey();
+ }
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Key";
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException,
+ IllegalStateException
+ {
+ System.arraycopy(key, 0, out, outOff, key.length);
+ return key.length;
+ }
+
+ public void reset()
+ {
+ }
+
+ }
+
+ private static class TestCase
+ {
+ private final byte[] key;
+ private final byte[] nonce;
+ private final byte[] message;
+ private final byte[] expectedMac;
+
+ public TestCase(String key, String nonce, String message, String expectedMac)
+ {
+ this.key = Hex.decode(key);
+ // nacl test case keys are not pre-clamped
+ Poly1305KeyGenerator.clamp(this.key);
+ this.nonce = (nonce == null) ? null : Hex.decode(nonce);
+ this.message = Hex.decode(message);
+ this.expectedMac = Hex.decode(expectedMac);
+ }
+ }
+
+ private static TestCase[] CASES = {
+ // Raw Poly1305
+ // onetimeauth.c from nacl-20110221
+ new TestCase("2539121d8e234e652d651fa4c8cff880eea6a7251c1e72916d11c2cb214d3c25", null,
+ "8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a"
+ + "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738"
+ + "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da"
+ + "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74e355a5",
+ "f3ffc7703f9400e52a7dfb4b3d3305d9"),
+
+ // Poly1305-AES
+ // Loop 1 of test-poly1305aes from poly1305aes-20050218
+ new TestCase("0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000", "", "66e94bd4ef8a2c3b884cfa59ca342b2e"),
+ new TestCase("f795bd4a52e29ed713d313fa20e98dbcf795bd0a50e29e0710d3130a20e98d0c",
+ "917cf69ebd68b2ec9b9fe9a3eadda692", "66f7", "5ca585c75e8f8f025e710cabc9a1508b"),
+ new TestCase("e69dae0aab9f91c03a325dcc9436fa903ef49901c8e11c000430d90ad45e7603",
+ "166450152e2394835606a9d1dd2cdc8b", "66f75c0e0c7a406586", "2924f51b9c2eff5df09db61dd03a9ca1"),
+ new TestCase("85a4ea91a7de0b0d96eed0d4bf6ecf1cda4afc035087d90e503f8f0ea08c3e0d",
+ "0b6ef7a0b8f8c738b0f8d5995415271f",
+ "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea",
+ "3c5a13adb18d31c64cc29972030c917d"),
+ new TestCase(
+ "25eb69bac5cdf7d6bfcee4d9d5507b82ca3c6a0da0a864024ca3090628c28e0d",
+ "046772a4f0a8de92e4f0d628cdb04484",
+ "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055de",
+ "fc5fb58dc65daf19b14d1d05da1064e8"),
+
+ // Specific test cases generated from test-poly1305aes from poly1305aes-20050218 that
+ // expose Java unsigned integer problems
+ new TestCase(
+ "95cc0e44d0b79a8856afcae1bec4fe3c" + "01bcb20bfc8b6e03609ddd09f44b060f",
+ null,
+ "66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055de"
+ + "fc3e0677d956b4c62664bac15962ab15d93ccbbc03aafdbde779162ed93b55361f0f8acaa41d50ef5175927fe79ea316186516eef15001cd04d3524a55"
+ + "e4fa3c5ca479d3aaa8a897c21807f721b6270ffc68b6889d81a116799f6aaa35d8e04c7a7dd5e6da2519e8759f54e906696f5772fee093283bcef7b930"
+ + "aed50323bcbc8c820c67422c1e16bdc022a9c0277c9d95fef0ea4ee11e2b27276da811523c5acb80154989f8a67ee9e3fa30b73b0c1c34bf46e3464d97"
+ + "7cd7fcd0ac3b82721080bb0d9b982ee2c77feee983d7ba35da88ce86955002940652ab63bc56fb16f994da2b01d74356509d7d1b6d7956b0e5a557757b"
+ + "d1ced2eef8650bc5b6d426108c1518abcbd0befb6a0d5fd57a3e2dbf31458eab63df66613653d4beae73f5c40eb438fbcfdcf4a4ba46320184b9ca0da4"
+ + "dfae77de7ccc910356caea3243f33a3c81b064b3b7cedc7435c223f664227215715980e6e0bb570d459ba80d7512dbe458c8f0f3f52d659b6e8eef19ee"
+ + "71aea2ced85c7a42ffca6522a62db49a2a46eff72bd7f7e0883acd087183f0627f3537a4d558754ed63358e8182bee196735b361dc9bd64d5e34e1074a"
+ + "855655d2974cc6fa1653754cf40f561d8c7dc526aab2908ec2d2b977cde1a1fb1071e32f40e049ea20f30368ba1592b4fe57fb51595d23acbdace324cd"
+ + "d78060a17187c662368854e915402d9b52fb21e984663e41c26a109437e162cfaf071b53f77e50000a5388ff183b82ce7a1af476c416d7d204157b3633"
+ + "b2f4ec077b699b032816997e37bceded8d4a04976fd7d0c0b029f290794c3be504c5242287ea2f831f11ed5690d92775cd6e863d7731fd4da687ebfb13"
+ + "df4c41dc0fb8", "ae345d555eb04d6947bb95c0965237e2"),
+ new TestCase(
+ "76fb3635a2dc92a1f768163ab12f2187" + "cd07fd0ef8c0be0afcbdb30af4af0009",
+ null,
+ "f05204a74f0f88a7fa1a95b84ec3d8ffb36fcdc7723ea65dfe7cd464e86e0abf6b9d51db3220cfd8496ad6e6d36ebee8d990f9ce0d3bb7f72b7ab5b3ab0a73240d11efe772c857021ae859db4933cdde4387b471d2ce700fef4b81087f8f47c307881fd83017afcd15b8d21edf9b704677f46df97b07e5b83f87c8abd90af9b1d0f9e2710e8ebd0d4d1c6a055abea861f42368bed94d9373e909c1d3715b221c16bc524c55c31ec3eab204850bb2474a84f9917038eff9d921130951391b5c54f09b5e1de833ea2cd7d3b306740abb7096d1e173da83427da2adddd3631eda30b54dbf487f2b082e8646f07d6e0a87e97522ca38d4ace4954bf3db6dd3a93b06fa18eb56856627ed6cffcd7ae26374554ca18ab8905f26331d323fe10e6e70624c7bc07a70f06ecd804b48f8f7e75e910165e1beb554f1f0ec7949c9c8d429a206b4d5c0653102249b6098e6b45fac2a07ff0220b0b8ae8f4c6bcc0c813a7cd141fa8b398b42575fc395747c5a0257ac41d6c1f434cfbf5dfe8349f5347ef6b60e611f5d6c3cbc20ca2555274d1934325824cef4809da293ea13f181929e2af025bbd1c9abdc3af93afd4c50a2854ade3887f4d2c8c225168052c16e74d76d2dd3e9467a2c5b8e15c06ffbffa42b8536384139f07e195a8c9f70f514f31dca4eb2cf262c0dcbde53654b6250a29efe21d54e83c80e005a1cad36d5934ff01c32e4bc5fe06d03064ff4a268517df4a94c759289f323734318cfa5d859d4ce9c16e63d02dff0896976f521607638535d2ee8dd3312e1ddc80a55d34fe829ab954c1ebd54d929954770f1be9d32b4c05003c5c9e97943b6431e2afe820b1e967b19843e5985a131b1100517cdc363799104af91e2cf3f53cb8fd003653a6dd8a31a3f9d566a7124b0ffe9695bcb87c482eb60106f88198f766a40bc0f4873c23653c5f9e7a8e446f770beb8034cf01d21028ba15ccee21a8db918c4829d61c88bfa927bc5def831501796c5b401a60a6b1b433c9fb905c8cd40412fffee81ab",
+ "045be28cc52009f506bdbfabedacf0b4"),
+
+ };
+
+ public String getName()
+ {
+ return "Poly1305";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testKeyGenerator();
+ testInit();
+ for (int i = 0; i < CASES.length; i++)
+ {
+ testCase(i);
+ }
+ testSequential();
+ testReset();
+ }
+
+ private void testCase(int i)
+ {
+ byte[] out = new byte[16];
+ TestCase tc = CASES[i];
+
+ final Mac mac;
+ if (tc.nonce == null)
+ {
+ // Raw Poly1305 test - don't do any transform on AES key part
+ mac = new Poly1305(new KeyEngine(16));
+ mac.init(new ParametersWithIV(new KeyParameter(tc.key), new byte[16]));
+ }
+ else
+ {
+ mac = new Poly1305(new AESFastEngine());
+ mac.init(new ParametersWithIV(new KeyParameter(tc.key), tc.nonce));
+ }
+ mac.update(tc.message, 0, tc.message.length);
+ mac.doFinal(out, 0);
+
+ if (!Arrays.areEqual(out, tc.expectedMac))
+ {
+ fail("Mismatched output " + i, new String(Hex.encode(tc.expectedMac)), new String(Hex.encode(out)));
+ }
+ }
+
+ private void testSequential()
+ {
+ // Sequential test, adapted from test-poly1305aes
+ int len;
+ byte[] kr = new byte[32];
+ byte[] m = new byte[MAXLEN];
+ byte[] n = new byte[16];
+ byte[] out = new byte[16];
+
+ int c = 0;
+ final Mac mac = new Poly1305(new AESFastEngine());
+ for (int loop = 0; loop < 13; loop++)
+ {
+ len = 0;
+ for (;;)
+ {
+ c++;
+ mac.init(new ParametersWithIV(new KeyParameter(kr), n));
+ mac.update(m, 0, len);
+ mac.doFinal(out, 0);
+
+ // if (c == 678)
+ // {
+ // TestCase tc = CASES[0];
+ //
+ // if (!Arrays.areEqual(tc.key, kr))
+ // {
+ // System.err.println("Key bad");
+ // System.err.println(new String(Hex.encode(tc.key)));
+ // System.err.println(new String(Hex.encode(kr)));
+ // System.exit(1);
+ // }
+ // if (!Arrays.areEqual(tc.nonce, n))
+ // {
+ // System.err.println("Nonce bad");
+ // System.exit(1);
+ // }
+ // System.out.printf("[%d] m: %s\n", c, new String(Hex.encode(m, 0, len)));
+ // System.out.printf("[%d] K: %s\n", c, new String(Hex.encodje(kr)));
+ // System.out.printf("[%d] N: %s\n", c, new String(Hex.encode(n)));
+ // System.out.printf("[%d] M: ", c);
+ // }
+ // System.out.printf("%d/%s\n", c, new String(Hex.encode(out)));
+
+ if (len >= MAXLEN)
+ break;
+ n[0] ^= loop;
+ for (int i = 0; i < 16; ++i)
+ n[i] ^= out[i];
+ if (len % 2 != 0)
+ for (int i = 0; i < 16; ++i)
+ kr[i] ^= out[i];
+ if (len % 3 != 0)
+ for (int i = 0; i < 16; ++i)
+ kr[i + 16] ^= out[i];
+ Poly1305KeyGenerator.clamp(kr);
+ m[len++] ^= out[0];
+ }
+ }
+ // Output after 13 loops as generated by poly1305 ref
+ if (c != 13013 || !Arrays.areEqual(out, Hex.decode("c96f60a23701a5b0fd2016f58cbe4f7e")))
+ {
+ fail("Sequential Poly1305 " + c, "c96f60a23701a5b0fd2016f58cbe4f7e", new String(Hex.encode(out)));
+ }
+ }
+
+ private void testReset()
+ {
+ CipherKeyGenerator gen = new Poly1305KeyGenerator();
+ gen.init(new KeyGenerationParameters(new SecureRandom(), 256));
+ byte[] k = gen.generateKey();
+
+ byte[] m = new byte[10000];
+ byte[] check = new byte[16];
+ byte[] out = new byte[16];
+
+ // Generate baseline
+ Mac poly = new Poly1305(new AESFastEngine());
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
+
+ poly.update(m, 0, m.length);
+ poly.doFinal(check, 0);
+
+ // Check reset after doFinal
+ poly.update(m, 0, m.length);
+ poly.doFinal(out, 0);
+
+ if (!Arrays.areEqual(check, out))
+ {
+ fail("Mac not reset after doFinal");
+ }
+
+ // Check reset
+ poly.update((byte)1);
+ poly.update((byte)2);
+ poly.reset();
+ poly.update(m, 0, m.length);
+ poly.doFinal(out, 0);
+
+ if (!Arrays.areEqual(check, out))
+ {
+ fail("Mac not reset after doFinal");
+ }
+
+ // Check init resets
+ poly.update((byte)1);
+ poly.update((byte)2);
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
+ poly.update(m, 0, m.length);
+ poly.doFinal(out, 0);
+
+ if (!Arrays.areEqual(check, out))
+ {
+ fail("Mac not reset after doFinal");
+ }
+ }
+
+ private void testInit()
+ {
+ CipherKeyGenerator gen = new Poly1305KeyGenerator();
+ gen.init(new KeyGenerationParameters(new SecureRandom(), 256));
+ byte[] k = gen.generateKey();
+
+ Mac poly = new Poly1305(new AESFastEngine());
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
+
+ try
+ {
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[15]));
+ fail("16 byte nonce required");
+ } catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ byte[] k2 = new byte[k.length - 1];
+ System.arraycopy(k, 0, k2, 0, k2.length);
+ poly.init(new ParametersWithIV(new KeyParameter(k2), new byte[16]));
+ fail("32 byte key required");
+ } catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ k[19] = (byte)0xFF;
+ poly.init(new ParametersWithIV(new KeyParameter(k), new byte[16]));
+ fail("Unclamped key should not be accepted.");
+ } catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ }
+
+ private void testKeyGenerator()
+ {
+ CipherKeyGenerator gen = new Poly1305KeyGenerator();
+ gen.init(new KeyGenerationParameters(new SecureRandom(), 256));
+ byte[] k = gen.generateKey();
+
+ if (k.length != 32)
+ {
+ fail("Poly1305 key should be 256 bits.");
+ }
+
+ try
+ {
+ Poly1305KeyGenerator.checkKey(k);
+ } catch (IllegalArgumentException e)
+ {
+ fail("Poly1305 key should be clamped on generation.");
+ }
+
+ byte[] k2 = new byte[k.length];
+ System.arraycopy(k, 0, k2, 0, k2.length);
+ Poly1305KeyGenerator.clamp(k);
+ if (!Arrays.areEqual(k, k2))
+ {
+ fail("Poly1305 key should be clamped on generation.");
+ }
+
+ try
+ {
+ k2[19] = (byte)0xff;
+ Poly1305KeyGenerator.checkKey(k2);
+ fail("Unclamped key should fail check.");
+ } catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ runTest(new Poly1305Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RC2Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC2Test.java
new file mode 100644
index 00000000..3c4e569b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC2Test.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.RC2Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.RC2Parameters;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * RC2 tester - vectors from ftp://ftp.isi.edu/in-notes/rfc2268.txt
+ *
+ * RFC 2268 "A Description of the RC2(r) Encryption Algorithm"
+ */
+public class RC2Test
+ extends CipherTest
+{
+ static BlockCipherVectorTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new RC2Engine(),
+ new RC2Parameters(Hex.decode("0000000000000000"), 63),
+ "0000000000000000", "ebb773f993278eff"),
+
+ new BlockCipherVectorTest(1, new RC2Engine(),
+ new RC2Parameters(Hex.decode("ffffffffffffffff"), 64),
+ "ffffffffffffffff", "278b27e42e2f0d49"),
+
+ new BlockCipherVectorTest(2, new RC2Engine(),
+ new RC2Parameters(Hex.decode("3000000000000000"), 64),
+ "1000000000000001", "30649edf9be7d2c2"),
+
+ new BlockCipherVectorTest(3, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88"), 64),
+ "0000000000000000", "61a8a244adacccf0"),
+
+ new BlockCipherVectorTest(4, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88bca90e90875a"), 64),
+ "0000000000000000", "6ccf4308974c267f"),
+
+ new BlockCipherVectorTest(5, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88bca90e90875a7f0f79c384627bafb2"), 64),
+ "0000000000000000", "1a807d272bbe5db1"),
+
+ new BlockCipherVectorTest(6, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88bca90e90875a7f0f79c384627bafb2"), 128),
+ "0000000000000000", "2269552ab0f85ca6"),
+
+ new BlockCipherVectorTest(7, new RC2Engine(),
+ new RC2Parameters(Hex.decode("88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e"), 129),
+ "0000000000000000", "5b78d3a43dfff1f1")
+ };
+
+ RC2Test()
+ {
+ super(tests, new RC2Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "RC2";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RC2Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RC2WrapTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC2WrapTest.java
new file mode 100644
index 00000000..0b52a60b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC2WrapTest.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.engines.RC2WrapEngine;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.RC2Parameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * RC2 wrap tester
+ */
+public class RC2WrapTest
+ implements Test
+{
+ private class RFCRandom
+ extends SecureRandom
+ {
+ public void nextBytes(
+ byte[] nextBytes)
+ {
+ System.arraycopy(Hex.decode("4845cce7fd1250"), 0, nextBytes, 0, nextBytes.length);
+ }
+ }
+
+ private TestResult wrapTest(
+ int id,
+ CipherParameters paramsWrap,
+ CipherParameters paramsUnwrap,
+ byte[] in,
+ byte[] out)
+ {
+ Wrapper wrapper = new RC2WrapEngine();
+
+ wrapper.init(true, paramsWrap);
+
+ try
+ {
+ byte[] cText = wrapper.wrap(in, 0, in.length);
+ if (!Arrays.areEqual(cText, out))
+ {
+ return new SimpleTestResult(false, getName() + ": failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed wrap test exception " + e.toString(), e);
+ }
+
+ wrapper.init(false, paramsUnwrap);
+
+ try
+ {
+ byte[] pText = wrapper.unwrap(out, 0, out.length);
+ if (!Arrays.areEqual(pText, in))
+ {
+ return new SimpleTestResult(false, getName() + ": failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+ catch (Exception e)
+ {
+ return new SimpleTestResult(false, getName() + ": failed unwrap test exception " + e.toString(), e);
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public TestResult perform()
+ {
+ byte[] kek1 = Hex.decode("fd04fd08060707fb0003fefffd02fe05");
+ byte[] iv1 = Hex.decode("c7d90059b29e97f7");
+ byte[] in1 = Hex.decode("b70a25fbc9d86a86050ce0d711ead4d9");
+ byte[] out1 = Hex.decode("70e699fb5701f7833330fb71e87c85a420bdc99af05d22af5a0e48d35f3138986cbaafb4b28d4f35");
+ //
+ // note the RFC 3217 test specifies a key to be used with an effective key size of
+ // 40 bits which is why it is done here - in practice nothing less than 128 bits should be used.
+ //
+ CipherParameters paramWrap = new ParametersWithRandom(new ParametersWithIV(new RC2Parameters(kek1, 40), iv1), new RFCRandom());
+ CipherParameters paramUnwrap = new RC2Parameters(kek1, 40);
+
+ TestResult result = wrapTest(1, paramWrap, paramUnwrap, in1, out1);
+
+ if (!result.isSuccessful())
+ {
+ return result;
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public String getName()
+ {
+ return "RC2Wrap";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ RC2WrapTest test = new RC2WrapTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RC4Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC4Test.java
new file mode 100644
index 00000000..e4d39743
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC4Test.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.RC4Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * RC4 Test
+ */
+public class RC4Test
+ extends SimpleTest
+{
+ StreamCipherVectorTest[] tests =
+ {
+ new StreamCipherVectorTest(0, new RC4Engine(),
+ new KeyParameter(Hex.decode("0123456789ABCDEF")),
+ "4e6f772069732074", "3afbb5c77938280d"),
+ new StreamCipherVectorTest(0, new RC4Engine(),
+ new KeyParameter(Hex.decode("0123456789ABCDEF")),
+ "68652074696d6520", "1cf1e29379266d59"),
+ new StreamCipherVectorTest(0, new RC4Engine(),
+ new KeyParameter(Hex.decode("0123456789ABCDEF")),
+ "666f7220616c6c20", "12fbb0c771276459")
+ };
+
+ public String getName()
+ {
+ return "RC4";
+ }
+
+ public void performTest()
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ tests[i].performTest();
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RC4Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RC5Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC5Test.java
new file mode 100644
index 00000000..0ffdc89a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC5Test.java
@@ -0,0 +1,188 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.RC532Engine;
+import org.bouncycastle.crypto.engines.RC564Engine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.RC5Parameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * RC5 tester - vectors from ftp://ftp.nordu.net/rfc/rfc2040.txt
+ *
+ * RFC 2040 "The RC5, RC5-CBC, RC5-CBC-Pad, and RC5-CTS Algorithms"
+ */
+public class RC5Test
+ implements Test
+{
+ BlockCipherVectorTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "7a7bba4d79111d1e"),
+ new BlockCipherVectorTest(1, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "797bba4d78111d1e"),
+ new BlockCipherVectorTest(2, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0000000000000001")),
+ "0000000000000000", "7a7bba4d79111d1f"),
+ new BlockCipherVectorTest(3, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0000000000000000")),
+ "0000000000000001", "7a7bba4d79111d1f"),
+ new BlockCipherVectorTest(4, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "8b9ded91ce7794a6"),
+ new BlockCipherVectorTest(5, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("11"), 1),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "2f759fe7ad86a378"),
+ new BlockCipherVectorTest(6, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 2),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "dca2694bf40e0788"),
+ new BlockCipherVectorTest(7, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00000000"), 2),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "dca2694bf40e0788"),
+ new BlockCipherVectorTest(8, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00000000"), 8),
+ Hex.decode("0000000000000000")),
+ "0000000000000000", "dcfe098577eca5ff"),
+ new BlockCipherVectorTest(9, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 8),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "9646fb77638f9ca8"),
+ new BlockCipherVectorTest(10, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 12),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "b2b3209db6594da4"),
+ new BlockCipherVectorTest(11, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 16),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "545f7f32a5fc3836"),
+ new BlockCipherVectorTest(12, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304"), 8),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "8285e7c1b5bc7402"),
+ new BlockCipherVectorTest(13, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304"), 12),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "fc586f92f7080934"),
+ new BlockCipherVectorTest(14, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304"), 16),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "cf270ef9717ff7c4"),
+ new BlockCipherVectorTest(15, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405060708"), 12),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "e493f1c1bb4d6e8c"),
+ new BlockCipherVectorTest(16, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405060708"), 8),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "5c4c041e0f217ac3"),
+ new BlockCipherVectorTest(17, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405060708"), 12),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "921f12485373b4f7"),
+ new BlockCipherVectorTest(18, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405060708"), 16),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "5ba0ca6bbe7f5fad"),
+ new BlockCipherVectorTest(19, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304050607081020304050607080"), 8),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "c533771cd0110e63"),
+ new BlockCipherVectorTest(20, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304050607081020304050607080"), 12),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "294ddb46b3278d60"),
+ new BlockCipherVectorTest(21, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("01020304050607081020304050607080"), 16),
+ Hex.decode("0102030405060708")),
+ "1020304050607080", "dad6bda9dfe8f7e8"),
+ new BlockCipherVectorTest(22, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405"), 12),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "97e0787837ed317f"),
+ new BlockCipherVectorTest(23, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405"), 8),
+ Hex.decode("0000000000000000")),
+ "ffffffffffffffff", "7875dbf6738c6478"),
+ new BlockCipherVectorTest(23, new CBCBlockCipher(new RC532Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("0102030405"), 8),
+ Hex.decode("7875dbf6738c6478")),
+ "0808080808080808", "8f34c3c681c99695"),
+ new BlockCipherVectorTest(640, new CBCBlockCipher(new RC564Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "9f09b98d3f6062d9d4d59973d00e0e63"),
+ new BlockCipherVectorTest(641, new CBCBlockCipher(new RC564Engine()),
+ new ParametersWithIV(
+ new RC5Parameters(Hex.decode("00"), 0),
+ Hex.decode("00000000000000000000000000000000")),
+ "ffffffffffffffffffffffffffffffff", "9e09b98d3f6062d9d3d59973d00e0e63")
+ };
+
+ public String getName()
+ {
+ return "RC5";
+ }
+
+ public TestResult perform()
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult res = tests[i].perform();
+
+ if (!res.isSuccessful())
+ {
+ return res;
+ }
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ RC5Test test = new RC5Test();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RC6Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC6Test.java
new file mode 100644
index 00000000..254c1378
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RC6Test.java
@@ -0,0 +1,64 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.RC6Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * RC6 Test - test vectors from AES Submitted RSA Reference implementation.
+ * ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/aes/rc6-unix-refc.tar
+ */
+public class RC6Test
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000",
+ "f71f65e7b80c0c6966fee607984b5cdf"),
+ new BlockCipherVectorTest(1, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("000000000000000000000000000000008000000000000000")),
+ "00000000000000000000000000000000",
+ "dd04c176440bbc6686c90aee775bd368"),
+ new BlockCipherVectorTest(2, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("000000000000000000000000000000000000001000000000")),
+ "00000000000000000000000000000000",
+ "937fe02d20fcb72f0f57201012b88ba4"),
+ new BlockCipherVectorTest(3, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("00000001000000000000000000000000")),
+ "00000000000000000000000000000000",
+ "8a380594d7396453771a1dfbe2914c8e"),
+ new BlockCipherVectorTest(4, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("1000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000000",
+ "11395d4bfe4c8258979ee2bf2d24dff4"),
+ new BlockCipherVectorTest(5, new RC6Engine(),
+ new KeyParameter(
+ Hex.decode("0000000000000000000000000000000000080000000000000000000000000000")),
+ "00000000000000000000000000000000",
+ "3d6f7e99f6512553bb983e8f75672b97")
+ };
+
+ RC6Test()
+ {
+ super(tests, new RC6Engine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "RC6";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RC6Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RFC3211WrapTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RFC3211WrapTest.java
new file mode 100644
index 00000000..f556cff6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RFC3211WrapTest.java
@@ -0,0 +1,220 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.Wrapper;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.engines.DESedeEngine;
+import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+import java.security.SecureRandom;
+
+/**
+ * Wrap Test based on RFC3211 test vectors
+ */
+public class RFC3211WrapTest
+ extends SimpleTest
+{
+ SecureRandom r1 = new SecureRandom()
+ {
+ int[] ints = { 0xC4, 0x36, 0xF5, 0x41 };
+ int count = 0;
+
+ public int nextInt()
+ {
+ return ints[count++];
+ }
+ };
+
+ SecureRandom r2 = new SecureRandom()
+ {
+ int[] ints = { 0xFA, 0x06, 0x0A, 0x45 };
+ int count = 0;
+
+ public int nextInt()
+ {
+ return ints[count++];
+ }
+ };
+
+ public String getName()
+ {
+ return "RFC3211Wrap";
+ }
+
+ private void wrapTest(
+ int id,
+ BlockCipher engine,
+ byte[] kek,
+ byte[] iv,
+ SecureRandom rand,
+ byte[] in,
+ byte[] out)
+ throws Exception
+ {
+ Wrapper wrapper = new RFC3211WrapEngine(engine);
+
+ wrapper.init(true, new ParametersWithRandom(new ParametersWithIV(new KeyParameter(kek), iv), rand));
+
+ byte[] cText = wrapper.wrap(in, 0, in.length);
+ if (!Arrays.areEqual(cText, out))
+ {
+ fail("failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText)));
+ }
+
+ wrapper.init(false, new ParametersWithIV(new KeyParameter(kek), iv));
+
+ byte[] pText = wrapper.unwrap(out, 0, out.length);
+ if (!Arrays.areEqual(pText, in))
+ {
+ fail("rfailed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText)));
+ }
+ }
+
+ private void testCorruption()
+ throws InvalidCipherTextException
+ {
+ byte[] kek = Hex.decode("D1DAA78615F287E6");
+ byte[] iv = Hex.decode("EFE598EF21B33D6D");
+
+ Wrapper wrapper = new RFC3211WrapEngine(new DESEngine());
+
+ wrapper.init(false, new ParametersWithIV(new KeyParameter(kek), iv));
+
+ byte[] block = Hex.decode("ff739D838C627C897323A2F8C436F541");
+ encryptBlock(kek, iv, block);
+
+ try
+ {
+ wrapper.unwrap(block, 0, block.length);
+
+ fail("bad length not detected");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals("wrapped key corrupted"))
+ {
+ fail("wrong exception on length");
+ }
+ }
+
+ block = Hex.decode("08639D838C627C897323A2F8C436F541");
+ testChecksum(kek, iv, block, wrapper);
+
+ block = Hex.decode("08736D838C627C897323A2F8C436F541");
+ testChecksum(kek, iv, block, wrapper);
+
+ block = Hex.decode("08739D638C627C897323A2F8C436F541");
+ testChecksum(kek, iv, block, wrapper);
+ }
+
+ private void testChecksum(byte[] kek, byte[] iv, byte[] block, Wrapper wrapper)
+ {
+ encryptBlock(kek, iv, block);
+
+ try
+ {
+ wrapper.unwrap(block, 0, block.length);
+
+ fail("bad checksum not detected");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals("wrapped key fails checksum"))
+ {
+ fail("wrong exception");
+ }
+ }
+ }
+
+ private void encryptBlock(byte[] key, byte[] iv, byte[] cekBlock)
+ {
+ BlockCipher engine = new CBCBlockCipher(new DESEngine());
+
+ engine.init(true, new ParametersWithIV(new KeyParameter(key), iv));
+
+ for (int i = 0; i < cekBlock.length; i += 8)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+
+ for (int i = 0; i < cekBlock.length; i += 8)
+ {
+ engine.processBlock(cekBlock, i, cekBlock, i);
+ }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ wrapTest(1, new DESEngine(), Hex.decode("D1DAA78615F287E6"), Hex.decode("EFE598EF21B33D6D"), r1, Hex.decode("8C627C897323A2F8"), Hex.decode("B81B2565EE373CA6DEDCA26A178B0C10"));
+ wrapTest(2, new DESedeEngine(), Hex.decode("6A8970BF68C92CAEA84A8DF28510858607126380CC47AB2D"), Hex.decode("BAF1CA7931213C4E"), r2,
+ Hex.decode("8C637D887223A2F965B566EB014B0FA5D52300A3F7EA40FFFC577203C71BAF3B"),
+ Hex.decode("C03C514ABDB9E2C5AAC038572B5E24553876B377AAFB82ECA5A9D73F8AB143D9EC74E6CAD7DB260C"));
+
+ testCorruption();
+
+ Wrapper wrapper = new RFC3211WrapEngine(new DESEngine());
+ ParametersWithIV params = new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]);
+ byte[] buf = new byte[16];
+
+ try
+ {
+ wrapper.init(true, params);
+
+ wrapper.unwrap(buf, 0, buf.length);
+
+ fail("failed unwrap state test.");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("unexpected exception: " + e, e);
+ }
+
+ try
+ {
+ wrapper.init(false, params);
+
+ wrapper.wrap(buf, 0, buf.length);
+
+ fail("failed unwrap state test.");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+
+ //
+ // short test
+ //
+ try
+ {
+ wrapper.init(false, params);
+
+ wrapper.unwrap(buf, 0, buf.length / 2);
+
+ fail("failed unwrap short test.");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // expected
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RFC3211WrapTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD128DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD128DigestTest.java
new file mode 100644
index 00000000..bdd622ca
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD128DigestTest.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+
+/**
+ * RIPEMD128 Digest Test
+ */
+public class RIPEMD128DigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ final static String[] digests = {
+ "cdf26213a150dc3ecb610f18f6b38b46",
+ "86be7afa339d0fc7cfc785e72f578d33",
+ "c14a12199c66e4ba84636b0f69144c77",
+ "9e327b3d6e523062afc1132d7df9d1b8",
+ "fd2aa607f71dc8f510714922b371834e",
+ "a1aa0689d0fafa2ddc22e88b49133a06",
+ "d1e959eb179c911faea4624c60c5c702",
+ "3f45ef194732c2dbb2c4a2c769795fa3"
+ };
+
+ final static String million_a_digest = "4a7f5723f954eba1216c9d8f6320431f";
+
+ RIPEMD128DigestTest()
+ {
+ super(new RIPEMD128Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new RIPEMD128Digest((RIPEMD128Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RIPEMD128DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD128HMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD128HMacTest.java
new file mode 100644
index 00000000..cc042c91
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD128HMacTest.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.RIPEMD128Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * RIPEMD128 HMac Test, test vectors from RFC 2286
+ */
+public class RIPEMD128HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "fbf61f9492aa4bbf81c172e84e0734db",
+ "875f828862b6b334b427c55f9f7ff09b",
+ "09f0b2846d2f543da363cbec8d62a38d",
+ "bdbbd7cf03e44b5aa60af815be4d2294",
+ "e79808f24b25fd031c155f0d551d9a3a",
+ "dc732928de98104a1f59d373c150acbb",
+ "5c6bec96793e16d40690c237635f30c5"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+ };
+
+ public String getName()
+ {
+ return "RIPEMD128HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new RIPEMD128Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed");
+ }
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ RIPEMD128HMacTest test = new RIPEMD128HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD160DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD160DigestTest.java
new file mode 100644
index 00000000..e92693d2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD160DigestTest.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+
+/**
+ * RIPEMD160 Digest Test
+ */
+public class RIPEMD160DigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ final static String[] digests = {
+ "9c1185a5c5e9fc54612808977ee8f548b2258d31",
+ "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe",
+ "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc",
+ "5d0689ef49d2fae572b881b123a85ffa21595f36",
+ "f71c27109c692c1b56bbdceb5b9d2865b3708dbc",
+ "12a053384a9c0c88e405a06c27dcf49ada62eb2b",
+ "b0e20b6e3116640286ed3a87a5713079b21f5189",
+ "9b752e45573d4b39f4dbd3323cab82bf63326bfb"
+ };
+
+ final static String million_a_digest = "52783243c1697bdbe16d37f97f68f08325dc1528";
+
+ RIPEMD160DigestTest()
+ {
+ super(new RIPEMD160Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new RIPEMD160Digest((RIPEMD160Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RIPEMD160DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD160HMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD160HMacTest.java
new file mode 100644
index 00000000..7e6e8132
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD160HMacTest.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.RIPEMD160Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * RIPEMD160 HMac Test, test vectors from RFC 2286
+ */
+public class RIPEMD160HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "24cb4bd67d20fc1a5d2ed7732dcc39377f0a5668",
+ "dda6c0213a485a9e24f4742064a7f033b43c4069",
+ "b0b105360de759960ab4f35298e116e295d8e7c1",
+ "d5ca862f4d21d5e610e18b4cf1beb97a4365ecf4",
+ "7619693978f91d90539ae786500ff3d8e0518e39",
+ "6466ca07ac5eac29e1bd523e5ada7605b791fd8b",
+ "69ea60798d71616cce5fd0871e23754cd75d5a0a"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+ };
+
+ public String getName()
+ {
+ return "RIPEMD160HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new RIPEMD160Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed");
+ }
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ RIPEMD160HMacTest test = new RIPEMD160HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD256DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD256DigestTest.java
new file mode 100644
index 00000000..4de70d8e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD256DigestTest.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.RIPEMD256Digest;
+
+/**
+ * RIPEMD128 Digest Test
+ */
+public class RIPEMD256DigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ final static String[] digests = {
+ "02ba4c4e5f8ecd1877fc52d64d30e37a2d9774fb1e5d026380ae0168e3c5522d",
+ "f9333e45d857f5d90a91bab70a1eba0cfb1be4b0783c9acfcd883a9134692925",
+ "afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65",
+ "87e971759a1ce47a514d5c914c392c9018c7c46bc14465554afcdf54a5070c0e",
+ "649d3034751ea216776bf9a18acc81bc7896118a5197968782dd1fd97d8d5133",
+ "3843045583aac6c8c8d9128573e7a9809afb2a0f34ccc36ea9e72f16f6368e3f",
+ "5740a408ac16b720b84424ae931cbb1fe363d1d0bf4017f1a89f7ea6de77a0b8",
+ "06fdcc7a409548aaf91368c06a6275b553e3f099bf0ea4edfd6778df89a890dd"
+ };
+
+ final static String million_a_digest = "ac953744e10e31514c150d4d8d7b677342e33399788296e43ae4850ce4f97978";
+
+ RIPEMD256DigestTest()
+ {
+ super(new RIPEMD256Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new RIPEMD256Digest((RIPEMD256Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RIPEMD256DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD320DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD320DigestTest.java
new file mode 100644
index 00000000..351b1b74
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RIPEMD320DigestTest.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.RIPEMD320Digest;
+
+/**
+ * RIPEMD320 Digest Test
+ */
+public class RIPEMD320DigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+ };
+
+ final static String[] digests = {
+ "22d65d5661536cdc75c1fdf5c6de7b41b9f27325ebc61e8557177d705a0ec880151c3a32a00899b8",
+ "ce78850638f92658a5a585097579926dda667a5716562cfcf6fbe77f63542f99b04705d6970dff5d",
+ "de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d",
+ "3a8e28502ed45d422f68844f9dd316e7b98533fa3f2a91d29f84d425c88d6b4eff727df66a7c0197",
+ "cabdb1810b92470a2093aa6bce05952c28348cf43ff60841975166bb40ed234004b8824463e6b009",
+ "d034a7950cf722021ba4b84df769a5de2060e259df4c9bb4a4268c0e935bbc7470a969c9d072a1ac",
+ "ed544940c86d67f250d232c30b7b3e5770e0c60c8cb9a4cafe3b11388af9920e1b99230b843c86a4",
+ "557888af5f6d8ed62ab66945c6d2a0a47ecd5341e915eb8fea1d0524955f825dc717e4a008ab2d42"
+ };
+
+ final static String million_a_digest = "bdee37f4371e20646b8b0d862dda16292ae36f40965e8c8509e63d1dbddecc503e2b63eb9245bb66";
+
+ RIPEMD320DigestTest()
+ {
+ super(new RIPEMD320Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new RIPEMD320Digest((RIPEMD320Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RIPEMD320DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RSABlindedTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSABlindedTest.java
new file mode 100644
index 00000000..7cb6e98a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSABlindedTest.java
@@ -0,0 +1,437 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.encodings.OAEPEncoding;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSABlindedEngine;
+import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
+import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class RSABlindedTest
+ extends SimpleTest
+{
+ static BigInteger mod = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
+ static BigInteger pubExp = new BigInteger("11", 16);
+ static BigInteger privExp = new BigInteger("92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619", 16);
+ static BigInteger p = new BigInteger("f75e80839b9b9379f1cf1128f321639757dba514642c206bbbd99f9a4846208b3e93fbbe5e0527cc59b1d4b929d9555853004c7c8b30ee6a213c3d1bb7415d03", 16);
+ static BigInteger q = new BigInteger("b892d9ebdbfc37e397256dd8a5d3123534d1f03726284743ddc6be3a709edb696fc40c7d902ed804c6eee730eee3d5b20bf6bd8d87a296813c87d3b3cc9d7947", 16);
+ static BigInteger pExp = new BigInteger("1d1a2d3ca8e52068b3094d501c9a842fec37f54db16e9a67070a8b3f53cc03d4257ad252a1a640eadd603724d7bf3737914b544ae332eedf4f34436cac25ceb5", 16);
+ static BigInteger qExp = new BigInteger("6c929e4e81672fef49d9c825163fec97c4b7ba7acb26c0824638ac22605d7201c94625770984f78a56e6e25904fe7db407099cad9b14588841b94f5ab498dded", 16);
+ static BigInteger crtCoef = new BigInteger("dae7651ee69ad1d081ec5e7188ae126f6004ff39556bde90e0b870962fa7b926d070686d8244fe5a9aa709a95686a104614834b0ada4b10f53197a5cb4c97339", 16);
+
+ static String input = "4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ //
+ // to check that we handling byte extension by big number correctly.
+ //
+ static String edgeInput = "ff6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ static byte[] oversizedSig = Hex.decode("01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] dudBlock = Hex.decode("000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] truncatedDataBlock = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] incorrectPadding = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] missingDataBlock = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
+
+ public String getName()
+ {
+ return "RSABlinded";
+ }
+
+ private void testStrictPKCS1Length(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ AsymmetricBlockCipher eng = new RSABlindedEngine();
+
+ eng.init(true, privParameters);
+
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(oversizedSig, 0, oversizedSig.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+
+ fail("oversized signature block not recognised");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals("block incorrect size"))
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+ }
+
+ //System.setProperty(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, "false");
+
+ System.getProperties().put(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, "false");
+ eng = new PKCS1Encoding(new RSABlindedEngine());
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ System.getProperties().remove(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY);
+ }
+
+ private void testTruncatedPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, truncatedDataBlock, "block truncated");
+ }
+
+ private void testDudPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, dudBlock, "unknown block type");
+ }
+
+ private void testWrongPaddingPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, incorrectPadding, "block padding incorrect");
+ }
+
+ private void testMissingDataPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, missingDataBlock, "no data in block");
+ }
+
+ private void checkForPKCS1Exception(RSAKeyParameters pubParameters, RSAKeyParameters privParameters, byte[] inputData, String expectedMessage)
+ {
+ AsymmetricBlockCipher eng = new RSABlindedEngine();
+
+ eng.init(true, privParameters);
+
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(inputData, 0, inputData.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+
+ fail("missing data block not recognised");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals(expectedMessage))
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+ }
+ }
+
+ private void testOAEP(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ //
+ // OAEP - public encrypt, private decrypt
+ //
+ AsymmetricBlockCipher eng = new OAEPEncoding(new RSABlindedEngine());
+ byte[] data = Hex.decode(input);
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed OAEP Test");
+ }
+ }
+
+ public void performTest()
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAPrivateCrtKeyParameters(mod, pubExp, privExp, p, q, pExp, qExp, crtCoef);
+ byte[] data = Hex.decode(edgeInput);
+
+ //
+ // RAW
+ //
+ AsymmetricBlockCipher eng = new RSABlindedEngine();
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!edgeInput.equals(new String(Hex.encode(data))))
+ {
+ fail("failed RAW edge Test");
+ }
+
+ data = Hex.decode(input);
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed RAW Test");
+ }
+
+ //
+ // PKCS1 - public encrypt, private decrypt
+ //
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(true, pubParameters);
+
+ if (eng.getOutputBlockSize() != ((PKCS1Encoding)eng).getUnderlyingCipher().getOutputBlockSize())
+ {
+ fail("PKCS1 output block size incorrect");
+ }
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed PKCS1 public/private Test");
+ }
+
+ //
+ // PKCS1 - private encrypt, public decrypt
+ //
+ eng = new PKCS1Encoding(((PKCS1Encoding)eng).getUnderlyingCipher());
+
+ eng.init(true, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed PKCS1 private/public Test");
+ }
+
+ //
+ // key generation test
+ //
+ RSAKeyPairGenerator pGen = new RSAKeyPairGenerator();
+ RSAKeyGenerationParameters genParam = new RSAKeyGenerationParameters(
+ BigInteger.valueOf(0x11), new SecureRandom(), 768, 25);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ eng = new RSABlindedEngine();
+
+ if (((RSAKeyParameters)pair.getPublic()).getModulus().bitLength() < 768)
+ {
+ fail("failed key generation (768) length test");
+ }
+
+ eng.init(true, pair.getPublic());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pair.getPrivate());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed key generation (768) Test");
+ }
+
+ genParam = new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 1024, 25);
+
+ pGen.init(genParam);
+ pair = pGen.generateKeyPair();
+
+ eng.init(true, pair.getPublic());
+
+ if (((RSAKeyParameters)pair.getPublic()).getModulus().bitLength() < 1024)
+ {
+ fail("failed key generation (1024) length test");
+ }
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pair.getPrivate());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed key generation (1024) test");
+ }
+
+ testOAEP(pubParameters, privParameters);
+ testStrictPKCS1Length(pubParameters, privParameters);
+ testDudPKCS1Block(pubParameters, privParameters);
+ testMissingDataPKCS1Block(pubParameters, privParameters);
+ testTruncatedPKCS1Block(pubParameters, privParameters);
+ testWrongPaddingPKCS1Block(pubParameters, privParameters);
+
+ try
+ {
+ new RSABlindedEngine().processBlock(new byte[]{ 1 }, 0, 1);
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RSABlindedTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java
new file mode 100644
index 00000000..605a2cac
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java
@@ -0,0 +1,55 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.crypto.signers.RSADigestSigner;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.test.SimpleTest;
+
+import java.math.BigInteger;
+
+public class RSADigestSignerTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "RSADigestSigner";
+ }
+
+ public void performTest() throws Exception
+ {
+ BigInteger rsaPubMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+ BigInteger rsaPubExp = new BigInteger(Base64.decode("EQ=="));
+ BigInteger rsaPrivMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+ BigInteger rsaPrivDP = new BigInteger(Base64.decode("JXzfzG5v+HtLJIZqYMUefJfFLu8DPuJGaLD6lI3cZ0babWZ/oPGoJa5iHpX4Ul/7l3s1PFsuy1GhzCdOdlfRcQ=="));
+ BigInteger rsaPrivDQ = new BigInteger(Base64.decode("YNdJhw3cn0gBoVmMIFRZzflPDNthBiWy/dUMSRfJCxoZjSnr1gysZHK01HteV1YYNGcwPdr3j4FbOfri5c6DUQ=="));
+ BigInteger rsaPrivExp = new BigInteger(Base64.decode("DxFAOhDajr00rBjqX+7nyZ/9sHWRCCp9WEN5wCsFiWVRPtdB+NeLcou7mWXwf1Y+8xNgmmh//fPV45G2dsyBeZbXeJwB7bzx9NMEAfedchyOwjR8PYdjK3NpTLKtZlEJ6Jkh4QihrXpZMO4fKZWUm9bid3+lmiq43FwW+Hof8/E="));
+ BigInteger rsaPrivP = new BigInteger(Base64.decode("AJ9StyTVW+AL/1s7RBtFwZGFBgd3zctBqzzwKPda6LbtIFDznmwDCqAlIQH9X14X7UPLokCDhuAa76OnDXb1OiE="));
+ BigInteger rsaPrivQ = new BigInteger(Base64.decode("AM3JfD79dNJ5A3beScSzPtWxx/tSLi0QHFtkuhtSizeXdkv5FSba7lVzwEOGKHmW829bRoNxThDy4ds1IihW1w0="));
+ BigInteger rsaPrivQinv = new BigInteger(Base64.decode("Lt0g7wrsNsQxuDdB8q/rH8fSFeBXMGLtCIqfOec1j7FEIuYA/ACiRDgXkHa0WgN7nLXSjHoy630wC5Toq8vvUg=="));
+ RSAKeyParameters rsaPublic = new RSAKeyParameters(false, rsaPubMod, rsaPubExp);
+ RSAPrivateCrtKeyParameters rsaPrivate = new RSAPrivateCrtKeyParameters(rsaPrivMod, rsaPubExp, rsaPrivExp, rsaPrivP, rsaPrivQ, rsaPrivDP, rsaPrivDQ, rsaPrivQinv);
+
+ byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+ RSADigestSigner signer = new RSADigestSigner(new SHA1Digest());
+ signer.init(true, rsaPrivate);
+ signer.update(msg, 0, msg.length);
+ byte[] sig = signer.generateSignature();
+
+ signer = new RSADigestSigner(new SHA1Digest(), X509ObjectIdentifiers.id_SHA1);
+ signer.init(false, rsaPublic);
+ signer.update(msg, 0, msg.length);
+ if (!signer.verifySignature(sig))
+ {
+ fail("RSA Digest Signer failed.");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new RSADigestSignerTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java
new file mode 100644
index 00000000..058f4682
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
+import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
+import org.bouncycastle.crypto.kems.RSAKeyEncapsulation;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Tests for the RSA Key Encapsulation Mechanism
+ */
+public class RSAKeyEncapsulationTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "RSAKeyEncapsulation";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ // Generate RSA key pair
+ RSAKeyPairGenerator rsaGen = new RSAKeyPairGenerator();
+ rsaGen.init(new RSAKeyGenerationParameters(BigInteger.valueOf(65537), new SecureRandom(), 1024, 5));
+ AsymmetricCipherKeyPair keys = rsaGen.generateKeyPair();
+
+ // Set RSA-KEM parameters
+ RSAKeyEncapsulation kem;
+ KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA1Digest());
+ SecureRandom rnd = new SecureRandom();
+ byte[] out = new byte[128];
+ KeyParameter key1, key2;
+
+ // Test RSA-KEM
+ kem = new RSAKeyEncapsulation(kdf, rnd);
+
+ kem.init(keys.getPublic());
+ key1 = (KeyParameter)kem.encrypt(out, 128);
+
+ kem.init(keys.getPrivate());
+ key2 = (KeyParameter)kem.decrypt(out, 128);
+
+ if (!areEqual(key1.getKey(), key2.getKey()))
+ {
+ fail("failed test");
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RSAKeyEncapsulationTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RSATest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSATest.java
new file mode 100644
index 00000000..a1ea3996
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RSATest.java
@@ -0,0 +1,498 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.AsymmetricBlockCipher;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.encodings.OAEPEncoding;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
+import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class RSATest
+ extends SimpleTest
+{
+ static BigInteger mod = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
+ static BigInteger pubExp = new BigInteger("11", 16);
+ static BigInteger privExp = new BigInteger("92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619", 16);
+ static BigInteger p = new BigInteger("f75e80839b9b9379f1cf1128f321639757dba514642c206bbbd99f9a4846208b3e93fbbe5e0527cc59b1d4b929d9555853004c7c8b30ee6a213c3d1bb7415d03", 16);
+ static BigInteger q = new BigInteger("b892d9ebdbfc37e397256dd8a5d3123534d1f03726284743ddc6be3a709edb696fc40c7d902ed804c6eee730eee3d5b20bf6bd8d87a296813c87d3b3cc9d7947", 16);
+ static BigInteger pExp = new BigInteger("1d1a2d3ca8e52068b3094d501c9a842fec37f54db16e9a67070a8b3f53cc03d4257ad252a1a640eadd603724d7bf3737914b544ae332eedf4f34436cac25ceb5", 16);
+ static BigInteger qExp = new BigInteger("6c929e4e81672fef49d9c825163fec97c4b7ba7acb26c0824638ac22605d7201c94625770984f78a56e6e25904fe7db407099cad9b14588841b94f5ab498dded", 16);
+ static BigInteger crtCoef = new BigInteger("dae7651ee69ad1d081ec5e7188ae126f6004ff39556bde90e0b870962fa7b926d070686d8244fe5a9aa709a95686a104614834b0ada4b10f53197a5cb4c97339", 16);
+
+ static String input = "4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ //
+ // to check that we handling byte extension by big number correctly.
+ //
+ static String edgeInput = "ff6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e";
+
+ static byte[] oversizedSig = Hex.decode("01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] dudBlock = Hex.decode("000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] truncatedDataBlock = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff004e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] incorrectPadding = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e");
+ static byte[] missingDataBlock = Hex.decode("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
+
+ public String getName()
+ {
+ return "RSA";
+ }
+
+ private void testStrictPKCS1Length(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ AsymmetricBlockCipher eng = new RSAEngine();
+
+ eng.init(true, privParameters);
+
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(oversizedSig, 0, oversizedSig.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+
+ fail("oversized signature block not recognised");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals("block incorrect size"))
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+ }
+
+ //System.setProperty(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, "false");
+
+ System.getProperties().put(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY, "false");
+ eng = new PKCS1Encoding(new RSAEngine());
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ System.getProperties().remove(PKCS1Encoding.STRICT_LENGTH_ENABLED_PROPERTY);
+ }
+
+ private void testTruncatedPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, truncatedDataBlock, "block truncated");
+ }
+
+ private void testDudPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, dudBlock, "unknown block type");
+ }
+
+ private void testWrongPaddingPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, incorrectPadding, "block padding incorrect");
+ }
+
+ private void testMissingDataPKCS1Block(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ checkForPKCS1Exception(pubParameters, privParameters, missingDataBlock, "no data in block");
+ }
+
+ private void checkForPKCS1Exception(RSAKeyParameters pubParameters, RSAKeyParameters privParameters, byte[] inputData, String expectedMessage)
+ {
+ AsymmetricBlockCipher eng = new RSAEngine();
+
+ eng.init(true, privParameters);
+
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(inputData, 0, inputData.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+
+ fail("missing data block not recognised");
+ }
+ catch (InvalidCipherTextException e)
+ {
+ if (!e.getMessage().equals(expectedMessage))
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+ }
+ }
+
+ private void testOAEP(RSAKeyParameters pubParameters, RSAKeyParameters privParameters)
+ {
+ //
+ // OAEP - public encrypt, private decrypt
+ //
+ AsymmetricBlockCipher eng = new OAEPEncoding(new RSAEngine());
+ byte[] data = Hex.decode(input);
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed OAEP Test");
+ }
+ }
+
+ private void zeroBlockTest(CipherParameters encParameters, CipherParameters decParameters)
+ {
+ AsymmetricBlockCipher eng = new PKCS1Encoding(new RSAEngine());
+
+ eng.init(true, encParameters);
+
+ if (eng.getOutputBlockSize() != ((PKCS1Encoding)eng).getUnderlyingCipher().getOutputBlockSize())
+ {
+ fail("PKCS1 output block size incorrect");
+ }
+
+ byte[] zero = new byte[0];
+ byte[] data = null;
+
+ try
+ {
+ data = eng.processBlock(zero, 0, zero.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, decParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!Arrays.areEqual(zero, data))
+ {
+ fail("failed PKCS1 zero Test");
+ }
+ }
+
+ public void performTest()
+ {
+ RSAKeyParameters pubParameters = new RSAKeyParameters(false, mod, pubExp);
+ RSAKeyParameters privParameters = new RSAPrivateCrtKeyParameters(mod, pubExp, privExp, p, q, pExp, qExp, crtCoef);
+ byte[] data = Hex.decode(edgeInput);
+
+ //
+ // RAW
+ //
+ AsymmetricBlockCipher eng = new RSAEngine();
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("RSA: failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!edgeInput.equals(new String(Hex.encode(data))))
+ {
+ fail("failed RAW edge Test");
+ }
+
+ data = Hex.decode(input);
+
+ eng.init(true, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed RAW Test");
+ }
+
+ //
+ // PKCS1 - public encrypt, private decrypt
+ //
+ eng = new PKCS1Encoding(eng);
+
+ eng.init(true, pubParameters);
+
+ if (eng.getOutputBlockSize() != ((PKCS1Encoding)eng).getUnderlyingCipher().getOutputBlockSize())
+ {
+ fail("PKCS1 output block size incorrect");
+ }
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed PKCS1 public/private Test");
+ }
+
+ //
+ // PKCS1 - private encrypt, public decrypt
+ //
+ eng = new PKCS1Encoding(((PKCS1Encoding)eng).getUnderlyingCipher());
+
+ eng.init(true, privParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pubParameters);
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed PKCS1 private/public Test");
+ }
+
+ zeroBlockTest(pubParameters, privParameters);
+ zeroBlockTest(privParameters, pubParameters);
+
+ //
+ // key generation test
+ //
+ RSAKeyPairGenerator pGen = new RSAKeyPairGenerator();
+ RSAKeyGenerationParameters genParam = new RSAKeyGenerationParameters(
+ BigInteger.valueOf(0x11), new SecureRandom(), 768, 25);
+
+ pGen.init(genParam);
+
+ AsymmetricCipherKeyPair pair = pGen.generateKeyPair();
+
+ eng = new RSAEngine();
+
+ if (((RSAKeyParameters)pair.getPublic()).getModulus().bitLength() < 768)
+ {
+ fail("failed key generation (768) length test");
+ }
+
+ eng.init(true, pair.getPublic());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pair.getPrivate());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed key generation (768) Test");
+ }
+
+ genParam = new RSAKeyGenerationParameters(BigInteger.valueOf(0x11), new SecureRandom(), 1024, 25);
+
+ pGen.init(genParam);
+ pair = pGen.generateKeyPair();
+
+ eng.init(true, pair.getPublic());
+
+ if (((RSAKeyParameters)pair.getPublic()).getModulus().bitLength() < 1024)
+ {
+ fail("failed key generation (1024) length test");
+ }
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ eng.init(false, pair.getPrivate());
+
+ try
+ {
+ data = eng.processBlock(data, 0, data.length);
+ }
+ catch (Exception e)
+ {
+ fail("failed - exception " + e.toString(), e);
+ }
+
+ if (!input.equals(new String(Hex.encode(data))))
+ {
+ fail("failed key generation (1024) test");
+ }
+
+ genParam = new RSAKeyGenerationParameters(
+ BigInteger.valueOf(0x11), new SecureRandom(), 16, 25);
+ pGen.init(genParam);
+
+ for (int i = 0; i < 100; ++i)
+ {
+ pair = pGen.generateKeyPair();
+ RSAPrivateCrtKeyParameters privKey = (RSAPrivateCrtKeyParameters) pair.getPrivate();
+ BigInteger pqDiff = privKey.getP().subtract(privKey.getQ()).abs();
+
+ if (pqDiff.bitLength() < 5)
+ {
+ fail("P and Q too close in RSA key pair");
+ }
+ }
+
+ testOAEP(pubParameters, privParameters);
+ testStrictPKCS1Length(pubParameters, privParameters);
+ testDudPKCS1Block(pubParameters, privParameters);
+ testMissingDataPKCS1Block(pubParameters, privParameters);
+ testTruncatedPKCS1Block(pubParameters, privParameters);
+ testWrongPaddingPKCS1Block(pubParameters, privParameters);
+
+ try
+ {
+ new RSAEngine().processBlock(new byte[]{ 1 }, 0, 1);
+ fail("failed initialisation check");
+ }
+ catch (IllegalStateException e)
+ {
+ // expected
+ }
+ }
+
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RSATest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RegressionTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RegressionTest.java
new file mode 100644
index 00000000..ea02a8f6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RegressionTest.java
@@ -0,0 +1,161 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+public class RegressionTest
+{
+ public static Test[] tests =
+ {
+ new AESTest(),
+ new AESLightTest(),
+ new AESFastTest(),
+ new AESWrapTest(),
+ new AESWrapPadTest(),
+ new DESTest(),
+ new DESedeTest(),
+ new ModeTest(),
+ new PaddingTest(),
+ new DHTest(),
+ new ElGamalTest(),
+ new DSATest(),
+ new ECTest(),
+ new DeterministicDSATest(),
+ new GOST3410Test(),
+ new ECGOST3410Test(),
+ new ECIESTest(),
+ new ECNRTest(),
+ new MacTest(),
+ new GOST28147MacTest(),
+ new RC2Test(),
+ new RC2WrapTest(),
+ new RC4Test(),
+ new RC5Test(),
+ new RC6Test(),
+ new RijndaelTest(),
+ new SerpentTest(),
+ new CamelliaTest(),
+ new CamelliaLightTest(),
+ new DigestRandomNumberTest(),
+ new SkipjackTest(),
+ new BlowfishTest(),
+ new TwofishTest(),
+ new Threefish256Test(),
+ new Threefish512Test(),
+ new Threefish1024Test(),
+ new SkeinDigestTest(),
+ new SkeinMacTest(),
+ new CAST5Test(),
+ new CAST6Test(),
+ new GOST28147Test(),
+ new IDEATest(),
+ new RSATest(),
+ new RSABlindedTest(),
+ new RSADigestSignerTest(),
+ new PSSBlindTest(),
+ new ISO9796Test(),
+ new ISO9797Alg3MacTest(),
+ new MD2DigestTest(),
+ new MD4DigestTest(),
+ new MD5DigestTest(),
+ new SHA1DigestTest(),
+ new SHA224DigestTest(),
+ new SHA256DigestTest(),
+ new SHA384DigestTest(),
+ new SHA512DigestTest(),
+ new SHA512t224DigestTest(),
+ new SHA512t256DigestTest(),
+ new SHA3DigestTest(),
+ new RIPEMD128DigestTest(),
+ new RIPEMD160DigestTest(),
+ new RIPEMD256DigestTest(),
+ new RIPEMD320DigestTest(),
+ new TigerDigestTest(),
+ new GOST3411DigestTest(),
+ new WhirlpoolDigestTest(),
+ new MD5HMacTest(),
+ new SHA1HMacTest(),
+ new SHA224HMacTest(),
+ new SHA256HMacTest(),
+ new SHA384HMacTest(),
+ new SHA512HMacTest(),
+ new RIPEMD128HMacTest(),
+ new RIPEMD160HMacTest(),
+ new OAEPTest(),
+ new PSSTest(),
+ new CTSTest(),
+ new NISTCTSTest(),
+ new CCMTest(),
+ new PKCS5Test(),
+ new PKCS12Test(),
+ new KDF1GeneratorTest(),
+ new KDF2GeneratorTest(),
+ new MGF1GeneratorTest(),
+ new HKDFGeneratorTest(),
+ new DHKEKGeneratorTest(),
+ new ECDHKEKGeneratorTest(),
+ new ShortenedDigestTest(),
+ new EqualsHashCodeTest(),
+ new TEATest(),
+ new XTEATest(),
+ new RFC3211WrapTest(),
+ new SEEDTest(),
+ new Salsa20Test(),
+ new XSalsa20Test(),
+ new ChaChaTest(),
+ new CMacTest(),
+ new EAXTest(),
+ new GCMTest(),
+ new GMacTest(),
+ new HCFamilyTest(),
+ new HCFamilyVecTest(),
+ new ISAACTest(),
+ new NoekeonTest(),
+ new VMPCKSA3Test(),
+ new VMPCMacTest(),
+ new VMPCTest(),
+ new Grainv1Test(),
+ new Grain128Test(),
+ //new NaccacheSternTest(),
+ new SRP6Test(),
+ new SCryptTest(),
+ new ResetTest(),
+ new NullTest(),
+ new DSTU4145Test(),
+ new SipHashTest(),
+ new Poly1305Test(),
+ new OCBTest(),
+ new NonMemoableDigestTest(),
+ new RSAKeyEncapsulationTest(),
+ new ECIESKeyEncapsulationTest(),
+ new HashCommitmentTest(),
+ new CipherStreamTest(),
+ new BlockCipherResetTest(),
+ new StreamCipherResetTest(),
+ new SM3DigestTest(),
+ new Shacal2Test(),
+ new KDFCounterGeneratorTest(),
+ new KDFDoublePipelineIteratorGeneratorTest(),
+ new KDFFeedbackGeneratorTest(),
+ new CramerShoupTest(),
+ new BCryptTest(),
+ new OpenBSDBCryptTest(),
+ new X931SignerTest()
+ };
+
+ public static void main(
+ String[] args)
+ {
+ for (int i = 0; i != tests.length; i++)
+ {
+ TestResult result = tests[i].perform();
+
+ if (result.getException() != null)
+ {
+ result.getException().printStackTrace();
+ }
+
+ System.out.println(result);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ResetTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ResetTest.java
new file mode 100644
index 00000000..efd0e06c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ResetTest.java
@@ -0,0 +1,99 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.DESEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class ResetTest
+ extends SimpleTest
+{
+ private static final byte[] input = Hex.decode("4e6f77206973207468652074696d6520666f7220616c6c20");
+ private static final byte[] output = Hex.decode("3fa40e8a984d48156a271787ab8883f9893d51ec4b563b53");
+ public String getName()
+ {
+ return "Reset";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ BufferedBlockCipher cipher = new BufferedBlockCipher(new DESEngine());
+
+ KeyParameter param = new KeyParameter(Hex.decode("0123456789abcdef"));
+
+ basicTrial(cipher, param);
+
+ cipher.init(false, param);
+
+ byte[] out = new byte[input.length];
+
+ int len2 = cipher.processBytes(output, 0, output.length - 1, out, 0);
+
+ try
+ {
+ cipher.doFinal(out, len2);
+ fail("no DataLengthException - short input");
+ }
+ catch (DataLengthException e)
+ {
+ // ignore
+ }
+
+ len2 = cipher.processBytes(output, 0, output.length, out, 0);
+
+ cipher.doFinal(out, len2);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal one got " + new String(Hex.encode(out)));
+ }
+
+ len2 = cipher.processBytes(output, 0, output.length - 1, out, 0);
+
+ try
+ {
+ cipher.doFinal(out, len2);
+ fail("no DataLengthException - short output");
+ }
+ catch (DataLengthException e)
+ {
+ // ignore
+ }
+
+ len2 = cipher.processBytes(output, 0, output.length, out, 0);
+
+ cipher.doFinal(out, len2);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal two got " + new String(Hex.encode(out)));
+ }
+ }
+
+ private void basicTrial(BufferedBlockCipher cipher, KeyParameter param)
+ throws InvalidCipherTextException
+ {
+ cipher.init(true, param);
+
+ byte[] out = new byte[input.length];
+
+ int len1 = cipher.processBytes(input, 0, input.length, out, 0);
+
+ cipher.doFinal(out, len1);
+
+ if (!areEqual(out, output))
+ {
+ fail("failed - " + "expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(out)));
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ResetTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RijndaelTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RijndaelTest.java
new file mode 100644
index 00000000..ca81dfb1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RijndaelTest.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.RijndaelEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test vectors from the NIST standard tests and Brian Gladman's vector set
+ * <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
+ */
+public class RijndaelTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "0EDD33D3C621E546455BD8BA1418BEC8"),
+ new BlockCipherVectorTest(1, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("00000000000000000000000000000080")),
+ "00000000000000000000000000000000", "172AEAB3D507678ECAF455C12587ADB7"),
+ new BlockCipherMonteCarloTest(2, 10000, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000000000000000", "C34C052CC0DA8D73451AFE5F03BE297F"),
+ new BlockCipherMonteCarloTest(3, 10000, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917")),
+ "355F697E8B868B65B25A04E18D782AFA", "ACC863637868E3E068D2FD6E3508454A"),
+ new BlockCipherVectorTest(4, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "6CD02513E8D4DC986B4AFE087A60BD0C"),
+ new BlockCipherMonteCarloTest(5, 10000, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("AAFE47EE82411A2BF3F6752AE8D7831138F041560631B114")),
+ "F3F6752AE8D7831138F041560631B114", "77BA00ED5412DFF27C8ED91F3C376172"),
+ new BlockCipherVectorTest(6, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "80000000000000000000000000000000", "DDC6BF790C15760D8D9AEB6F9A75FD4E"),
+ new BlockCipherMonteCarloTest(7, 10000, new RijndaelEngine(128),
+ new KeyParameter(Hex.decode("28E79E2AFC5F7745FCCABE2F6257C2EF4C4EDFB37324814ED4137C288711A386")),
+ "C737317FE0846F132B23C8C2A672CE22", "E58B82BFBA53C0040DC610C642121168"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c")),
+ "3243f6a8885a308d313198a2e03707344a409382", "16e73aec921314c29df905432bc8968ab64b1f51"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160")),
+ "3243f6a8885a308d313198a2e03707344a409382", "0553eb691670dd8a5a5b5addf1aa7450f7a0e587"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5")),
+ "3243f6a8885a308d313198a2e03707344a409382", "73cd6f3423036790463aa9e19cfcde894ea16623"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d90")),
+ "3243f6a8885a308d313198a2e03707344a409382", "601b5dcd1cf4ece954c740445340bf0afdc048df"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(160),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe")),
+ "3243f6a8885a308d313198a2e03707344a409382", "579e930b36c1529aa3e86628bacfe146942882cf"),
+ new BlockCipherVectorTest(8, new RijndaelEngine(192),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d", "b24d275489e82bb8f7375e0d5fcdb1f481757c538b65148a"),
+ new BlockCipherVectorTest(9, new RijndaelEngine(192),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d", "725ae43b5f3161de806a7c93e0bca93c967ec1ae1b71e1cf"),
+ new BlockCipherVectorTest(10, new RijndaelEngine(192),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d90")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d", "bbfc14180afbf6a36382a061843f0b63e769acdc98769130"),
+ new BlockCipherVectorTest(11, new RijndaelEngine(192),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d", "0ebacf199e3315c2e34b24fcc7c46ef4388aa475d66c194c"),
+ new BlockCipherVectorTest(12, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "b0a8f78f6b3c66213f792ffd2a61631f79331407a5e5c8d3793aceb1"),
+ new BlockCipherVectorTest(13, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "08b99944edfce33a2acb131183ab0168446b2d15e958480010f545e3"),
+ new BlockCipherVectorTest(14, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "be4c597d8f7efe22a2f7e5b1938e2564d452a5bfe72399c7af1101e2"),
+ new BlockCipherVectorTest(15, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d90")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "ef529598ecbce297811b49bbed2c33bbe1241d6e1a833dbe119569e8"),
+ new BlockCipherVectorTest(16, new RijndaelEngine(224),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa9", "02fafc200176ed05deb8edb82a3555b0b10d47a388dfd59cab2f6c11"),
+ new BlockCipherVectorTest(17, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "7d15479076b69a46ffb3b3beae97ad8313f622f67fedb487de9f06b9ed9c8f19"),
+ new BlockCipherVectorTest(18, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "514f93fb296b5ad16aa7df8b577abcbd484decacccc7fb1f18dc567309ceeffd"),
+ new BlockCipherVectorTest(19, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "5d7101727bb25781bf6715b0e6955282b9610e23a43c2eb062699f0ebf5887b2"),
+ new BlockCipherVectorTest(20, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d90")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "d56c5a63627432579e1dd308b2c8f157b40a4bfb56fea1377b25d3ed3d6dbf80"),
+ new BlockCipherVectorTest(21, new RijndaelEngine(256),
+ new KeyParameter(Hex.decode("2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe")),
+ "3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c8", "a49406115dfb30a40418aafa4869b7c6a886ff31602a7dd19c889dc64f7e4e7a")
+ };
+
+ RijndaelTest()
+ {
+ super(tests, new RijndaelEngine(128), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "Rijndael";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new RijndaelTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SCryptTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SCryptTest.java
new file mode 100644
index 00000000..b017a1da
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SCryptTest.java
@@ -0,0 +1,144 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import org.bouncycastle.crypto.generators.SCrypt;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/*
+ * scrypt test vectors from "Stronger Key Derivation Via Sequential Memory-hard Functions" Appendix B.
+ * (http://www.tarsnap.com/scrypt/scrypt.pdf)
+ */
+public class SCryptTest extends SimpleTest
+{
+ public String getName()
+ {
+ return "SCrypt";
+ }
+
+ public void performTest() throws Exception
+ {
+ testParameters();
+ testVectors();
+ }
+
+ public void testParameters()
+ {
+ checkOK("Minimal values", new byte[0], new byte[0], 2, 1, 1, 1);
+ checkIllegal("Cost parameter must be > 1", new byte[0], new byte[0], 1, 1, 1, 1);
+ checkOK("Cost parameter 65536 OK for r == 1", new byte[0], new byte[0], 65536, 1, 1, 1);
+ checkIllegal("Cost parameter must <= 65536 for r == 1", new byte[0], new byte[0], 65537, 1, 1, 1);
+ checkIllegal("Block size must be >= 1", new byte[0], new byte[0], 2, 0, 2, 1);
+ checkIllegal("Parallelisation parameter must be >= 1", new byte[0], new byte[0], 2, 1, 0, 1);
+ // checkOK("Parallelisation parameter 65535 OK for r = 4", new byte[0], new byte[0], 2, 32,
+ // 65535, 1);
+ checkIllegal("Parallelisation parameter must be < 65535 for r = 4", new byte[0], new byte[0], 2, 32, 65536, 1);
+
+ checkIllegal("Len parameter must be > 1", new byte[0], new byte[0], 2, 1, 1, 0);
+ }
+
+ private void checkOK(String msg, byte[] pass, byte[] salt, int N, int r, int p, int len)
+ {
+ try
+ {
+ SCrypt.generate(pass, salt, N, r, p, len);
+ }
+ catch (IllegalArgumentException e)
+ {
+ e.printStackTrace();
+ fail(msg);
+ }
+ }
+
+ private void checkIllegal(String msg, byte[] pass, byte[] salt, int N, int r, int p, int len)
+ {
+ try
+ {
+ SCrypt.generate(pass, salt, N, r, p, len);
+ fail(msg);
+ }
+ catch (IllegalArgumentException e)
+ {
+ // e.printStackTrace();
+ }
+ }
+
+ public void testVectors()
+ throws Exception
+ {
+ BufferedReader br = new BufferedReader(new InputStreamReader(
+ getClass().getResourceAsStream("SCryptTestVectors.txt")));
+
+ int count = 0;
+ String line = br.readLine();
+
+ while (line != null)
+ {
+ ++count;
+ String header = line;
+ StringBuffer data = new StringBuffer();
+
+ while (!isEndData(line = br.readLine()))
+ {
+ for (int i = 0; i != line.length(); i++)
+ {
+ if (line.charAt(i) != ' ')
+ {
+ data.append(line.charAt(i));
+ }
+ }
+ }
+
+ int start = header.indexOf('(') + 1;
+ int limit = header.lastIndexOf(')');
+ String argStr = header.substring(start, limit);
+ String[] args = Strings.split(argStr, ',');
+
+ byte[] P = extractQuotedString(args[0]);
+ byte[] S = extractQuotedString(args[1]);
+ int N = extractInteger(args[2]);
+ int r = extractInteger(args[3]);
+ int p = extractInteger(args[4]);
+ int dkLen = extractInteger(args[5]);
+ byte[] expected = Hex.decode(data.toString());
+
+ // This skips very expensive test case(s), remove check to re-enable
+ if (N <= 16384)
+ {
+ byte[] result = SCrypt.generate(P, S, N, r, p, dkLen);
+
+ if (!areEqual(expected, result))
+ {
+ fail("Result does not match expected value in test case " + count);
+ }
+ }
+ }
+
+ br.close();
+ }
+
+ private static boolean isEndData(String line)
+ {
+ return line == null || line.startsWith("scrypt");
+ }
+
+ private static byte[] extractQuotedString(String arg)
+ {
+ arg = arg.trim();
+ arg = arg.substring(1, arg.length() - 1);
+ return Strings.toByteArray(arg);
+ }
+
+ private static int extractInteger(String arg)
+ {
+ return Integer.parseInt(arg.trim());
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SCryptTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SEEDTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SEEDTest.java
new file mode 100644
index 00000000..4aa955b2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SEEDTest.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.SEEDEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * SEED tester - vectors http://www.ietf.org/rfc/rfc4009.txt
+ */
+public class SEEDTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "000102030405060708090a0b0c0d0e0f",
+ "5EBAC6E0054E166819AFF1CC6D346CDB"),
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "00000000000000000000000000000000",
+ "c11f22f20140505084483597e4370f43"),
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("4706480851E61BE85D74BFB3FD956185")),
+ "83A2F8A288641FB9A4E9A5CC2F131C7D",
+ "EE54D13EBCAE706D226BC3142CD40D4A"),
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("28DBC3BC49FFD87DCFA509B11D422BE7")),
+ "B41E6BE2EBA84A148E2EED84593C5EC7",
+ "9B9B7BFCD1813CB95D0B3618F40F5122"),
+ new BlockCipherVectorTest(0, new SEEDEngine(),
+ new KeyParameter(Hex.decode("0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E")),
+ "0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E",
+ "8296F2F1B007AB9D533FDEE35A9AD850"),
+ };
+
+ SEEDTest()
+ {
+ super(tests, new SEEDEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "SEED";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SEEDTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA1DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA1DigestTest.java
new file mode 100644
index 00000000..38b9b807
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA1DigestTest.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+
+/**
+ * standard vector test for SHA-1 from "Handbook of Applied Cryptography", page 345.
+ */
+public class SHA1DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghijklmnopqrstuvwxyz"
+ };
+
+ private static String[] digests =
+ {
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ "a9993e364706816aba3e25717850c26c9cd0d89d",
+ "32d10c7b8cf96570ca04ce37f2a19d84240d3a89"
+ };
+
+ SHA1DigestTest()
+ {
+ super(new SHA1Digest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA1Digest((SHA1Digest)digest);
+ }
+
+ protected Digest cloneDigest(byte[] encodedState)
+ {
+ return new SHA1Digest(encodedState);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA1DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA1HMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA1HMacTest.java
new file mode 100644
index 00000000..72a5fdc9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA1HMacTest.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * SHA1 HMac Test, test vectors from RFC 2202
+ */
+public class SHA1HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F"
+ };
+
+ final static String[] digests = {
+ "b617318655057264e28bc0b6fb378c8ef146be00",
+ "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
+ "125d7342b9ac11cd91a39af48aa17b4f63f175d3",
+ "4c9007f4026250c6bc8414f9bf50c86c2d7235da",
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112",
+ "e8e99d0f45237d786d6bbaa7965c7808bbff1a91",
+ "5FD596EE78D5553C8FF4E72D266DFD192366DA29"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ "Sample message for keylen=blocklen"
+ };
+
+ public String getName()
+ {
+ return "SHA1HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA1Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed");
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA1HMacTest test = new SHA1HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA224DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA224DigestTest.java
new file mode 100644
index 00000000..d14f87d9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA224DigestTest.java
@@ -0,0 +1,59 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA224Digest;
+
+/**
+ * standard vector test for SHA-224 from RFC 3874 - only the last three are in
+ * the RFC.
+ */
+public class SHA224DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ };
+
+ private static String[] digests =
+ {
+ "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
+ "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5",
+ "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7",
+ "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67";
+
+ SHA224DigestTest()
+ {
+ super(new SHA224Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA224Digest((SHA224Digest)digest);
+ }
+
+ protected Digest cloneDigest(byte[] encodedState)
+ {
+ return new SHA224Digest(encodedState);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA224DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA224HMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA224HMacTest.java
new file mode 100644
index 00000000..52abb16f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA224HMacTest.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.SHA224Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * SHA224 HMac Test
+ */
+public class SHA224HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22",
+ "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44",
+ "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea",
+ "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a",
+ "0e2aea68a90c8d37c988bcdb9fca6fa8099cd857c7ec4a1815cac54c",
+ "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e",
+ "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."
+ };
+
+ public String getName()
+ {
+ return "SHA224HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA224Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed got -" + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA224HMacTest test = new SHA224HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA256DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA256DigestTest.java
new file mode 100644
index 00000000..8a35ce42
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA256DigestTest.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+
+/**
+ * standard vector test for SHA-256 from FIPS Draft 180-2.
+ *
+ * Note, the first two vectors are _not_ from the draft, the last three are.
+ */
+public class SHA256DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ };
+
+ private static String[] digests =
+ {
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+ "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0";
+
+ SHA256DigestTest()
+ {
+ super(new SHA256Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA256Digest((SHA256Digest)digest);
+ }
+
+ protected Digest cloneDigest(byte[] encodedState)
+ {
+ return new SHA256Digest(encodedState);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA256DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA256HMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA256HMacTest.java
new file mode 100644
index 00000000..82ab1dc7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA256HMacTest.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * SHA256 HMac Test
+ */
+public class SHA256HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
+ "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
+ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
+ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
+ "a3b6167473100ee06e0c796c2955552bfa6f7c0a6a8aef8b93f860aab0cd20c5",
+ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
+ "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."
+ };
+
+ public String getName()
+ {
+ return "SHA256HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA256Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed got -" + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA256HMacTest test = new SHA256HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA384DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA384DigestTest.java
new file mode 100644
index 00000000..7b5f149f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA384DigestTest.java
@@ -0,0 +1,59 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA384Digest;
+
+/**
+ * standard vector test for SHA-384 from FIPS Draft 180-2.
+ *
+ * Note, the first two vectors are _not_ from the draft, the last three are.
+ */
+public class SHA384DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+ };
+
+ private static String[] digests =
+ {
+ "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
+ "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31",
+ "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7",
+ "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039"
+ };
+
+ static private String million_a_digest = "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985";
+
+ SHA384DigestTest()
+ {
+ super(new SHA384Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA384Digest((SHA384Digest)digest);
+ }
+
+ protected Digest cloneDigest(byte[] encodedState)
+ {
+ return new SHA384Digest(encodedState);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA384DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA384HMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA384HMacTest.java
new file mode 100644
index 00000000..fe903a39
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA384HMacTest.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.SHA384Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * SHA384 HMac Test
+ */
+public class SHA384HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6",
+ "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649",
+ "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27",
+ "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb",
+ "3abf34c3503b2a23a46efc619baef897f4c8e42c934ce55ccbae9740fcbc1af4ca62269e2a37cd88ba926341efe4aeea",
+ "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952",
+ "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."
+ };
+
+ public String getName()
+ {
+ return "SHA384HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA384Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed got -" + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA384HMacTest test = new SHA384HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA3DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA3DigestTest.java
new file mode 100644
index 00000000..260d457a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA3DigestTest.java
@@ -0,0 +1,363 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.SHA3Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * SHA3 Digest Test
+ */
+public class SHA3DigestTest
+ extends SimpleTest
+{
+ final static String[] messages = {
+ "",
+ "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67",
+ "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"
+ };
+
+ final static String[] digests288 = { // the default settings
+ "6753e3380c09e385d0339eb6b050a68f66cfd60a73476e6fd6adeb72f5edd7c6f04a5d01", // message[0]
+ "0bbe6afae0d7e89054085c1cc47b1689772c89a41796891e197d1ca1b76f288154933ded", // message[1]
+ "82558a209b960ddeb531e6dcb281885b2400ca160472462486e79f071e88a3330a8a303d", // message[2]
+ "94049e1ad7ef5d5b0df2b880489e7ab09ec937c3bfc1b04470e503e1ac7b1133c18f86da", // 64k a-test
+ "a9cb5a75b5b81b7528301e72553ed6770214fa963956e790528afe420de33c074e6f4220", // random alphabet test
+ "eadaf5ba2ad6a2f6f338fce0e1efdad2a61bb38f6be6068b01093977acf99e97a5d5827c" // extremely long data test
+ };
+
+ final static String[] digests224 = {
+ "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd",
+ "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe",
+ "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab",
+ "f621e11c142fbf35fa8c22841c3a812ba1e0151be4f38d80b9f1ff53",
+ "68b5fc8c87193155bba68a2485377e809ee4f81a85ef023b9e64add0",
+ "c42e4aee858e1a8ad2976896b9d23dd187f64436ee15969afdbc68c5"
+ };
+
+ final static String[] digests256 = {
+ "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15",
+ "578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d",
+ "0047a916daa1f92130d870b542e22d3108444f5a7e4429f05762fb647e6ed9ed",
+ "db368762253ede6d4f1db87e0b799b96e554eae005747a2ea687456ca8bcbd03",
+ "5f313c39963dcf792b5470d4ade9f3a356a3e4021748690a958372e2b06f82a4"
+ };
+
+ final static String[] digests384 = {
+ "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff",
+ "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3",
+ "9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b",
+ "c704cfe7a1a53208ca9526cd24251e0acdc252ecd978eee05acd16425cfb404ea81f5a9e2e5e97784d63ee6a0618a398",
+ "d4fe8586fd8f858dd2e4dee0bafc19b4c12b4e2a856054abc4b14927354931675cdcaf942267f204ea706c19f7beefc4",
+ "9b7168b4494a80a86408e6b9dc4e5a1837c85dd8ff452ed410f2832959c08c8c0d040a892eb9a755776372d4a8732315"
+ };
+
+ final static String[] digests512 = {
+ "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e",
+ "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609",
+ "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760",
+ "34341ead153aa1d1fdcf6cf624c2b4f6894b6fd16dc38bd4ec971ac0385ad54fafcb2e0ed86a1e509456f4246fdcb02c3172824cd649d9ad54c51f7fb49ea67c",
+ "dc44d4f4d36b07ab5fc04016cbe53548e5a7778671c58a43cb379fd00c06719b8073141fc22191ffc3db5f8b8983ae8341fa37f18c1c969664393aa5ceade64e",
+ "3e122edaf37398231cfaca4c7c216c9d66d5b899ec1d7ac617c40c7261906a45fc01617a021e5da3bd8d4182695b5cb785a28237cbb167590e34718e56d8aab8"
+ };
+
+ // test vectors from http://www.di-mgt.com.au/hmac_sha3_testvectors.html
+ final static byte[][] macKeys =
+ {
+ Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
+ Hex.decode("4a656665"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+ Hex.decode("0102030405060708090a0b0c0d0e0f10111213141516171819"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaa"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaa"),
+ Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+ };
+
+ final static String[] macData =
+ {
+ "4869205468657265",
+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
+ "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" +
+ "dddddddddddddddddddddddddddddddddddd",
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" +
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" +
+ "65204b6579202d2048617368204b6579204669727374",
+ "5468697320697320612074657374207573696e672061206c6172676572207468" +
+ "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
+ "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
+ "647320746f20626520686173686564206265666f7265206265696e6720757365" +
+ "642062792074686520484d414320616c676f726974686d2e",
+ "5468697320697320612074657374207573696e672061206c6172676572207468" +
+ "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" +
+ "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" +
+ "647320746f20626520686173686564206265666f7265206265696e6720757365\n" +
+ "642062792074686520484d414320616c676f726974686d2e"
+ };
+
+ final static String[] mac224 =
+ {
+ "b73d595a2ba9af815e9f2b4e53e78581ebd34a80b3bbaac4e702c4cc",
+ "e824fec96c074f22f99235bb942da1982664ab692ca8501053cbd414",
+ "770df38c99d6e2bacd68056dcfe07d4c89ae20b2686a6185e1faa449",
+ "305a8f2dfb94bad28861a03cbc4d590febe775c58cb4961c28428a0b",
+ "e7a52dfa45f95a217c100066b239aa8ad519be9b35d667268b1b57ff",
+ "ba13009405a929f398b348885caa5419191bb948ada32194afc84104",
+ "92649468be236c3c72c189909c063b13f994be05749dc91310db639e"
+ };
+
+ final static String[] mac256 =
+ {
+ "9663d10c73ee294054dc9faf95647cb99731d12210ff7075fb3d3395abfb9821",
+ "aa9aed448c7abc8b5e326ffa6a01cdedf7b4b831881468c044ba8dd4566369a1",
+ "95f43e50f8df80a21977d51a8db3ba572dcd71db24687e6f86f47c1139b26260",
+ "6331ba9b4af5804a68725b3663eb74814494b63c6093e35fb320a85d507936fd",
+ "b4d0cdee7ec2ba81a88b86918958312300a15622377929a054a9ce3ae1fac2b6",
+ "1fdc8cb4e27d07c10d897dec39c217792a6e64fa9c63a77ce42ad106ef284e02",
+ "fdaa10a0299aecff9bb411cf2d7748a4022e4a26be3fb5b11b33d8c2b7ef5484"
+ };
+
+ final static String[] mac384 =
+ {
+ "892dfdf5d51e4679bf320cd16d4c9dc6f749744608e003add7fba894acff87361efa4e5799be06b6461f43b60ae97048",
+ "5af5c9a77a23a6a93d80649e562ab77f4f3552e3c5caffd93bdf8b3cfc6920e3023fc26775d9df1f3c94613146ad2c9d",
+ "4243c29f2201992ff96441e3b91ff81d8c601d706fbc83252684a4bc51101ca9b2c06ddd03677303c502ac5331752a3c",
+ "b730724d3d4090cda1be799f63acbbe389fef7792fc18676fa5453aab398664650ed029c3498bbe8056f06c658e1e693",
+ "d62482ef601d7847439b55236e9679388ffcd53c62cd126f39be6ea63de762e26cd5974cb9a8de401b786b5555040f6f",
+ "4860ea191ac34994cf88957afe5a836ef36e4cc1a66d75bf77defb7576122d75f60660e4cf731c6effac06402787e2b9",
+ "fe9357e3cfa538eb0373a2ce8f1e26ad6590afdaf266f1300522e8896d27e73f654d0631c8fa598d4bb82af6b744f4f5"
+ };
+
+ final static String[] mac512 =
+ {
+ "8852c63be8cfc21541a4ee5e5a9a852fc2f7a9adec2ff3a13718ab4ed81aaea0b87b7eb397323548e261a64e7fc75198f6663a11b22cd957f7c8ec858a1c7755",
+ "c2962e5bbe1238007852f79d814dbbecd4682e6f097d37a363587c03bfa2eb0859d8d9c701e04cececfd3dd7bfd438f20b8b648e01bf8c11d26824b96cebbdcb",
+ "eb0ed9580e0ec11fc66cbb646b1be904eaff6da4556d9334f65ee4b2c85739157bae9027c51505e49d1bb81cfa55e6822db55262d5a252c088a29a5e95b84a66",
+ "b46193bb59f4f696bf702597616da91e2a4558a593f4b015e69141ba81e1e50ea580834c2b87f87baa25a3a03bfc9bb389847f2dc820beae69d30c4bb75369cb",
+ "d05888a6ebf8460423ea7bc85ea4ffda847b32df32291d2ce115fd187707325c7ce4f71880d91008084ce24a38795d20e6a28328a0f0712dc38253370da3ebb5",
+ "2c6b9748d35c4c8db0b4407dd2ed2381f133bdbd1dfaa69e30051eb6badfcca64299b88ae05fdbd3dd3dd7fe627e42e39e48b0fe8c7f1e85f2dbd52c2d753572",
+ "6adc502f14e27812402fc81a807b28bf8a53c87bea7a1df6256bf66f5de1a4cb741407ad15ab8abc136846057f881969fbb159c321c904bfb557b77afb7778c8"
+ };
+
+ final static KeyParameter truncKey = new KeyParameter(Hex.decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"));
+ final static byte[] truncData = Hex.decode("546573742057697468205472756e636174696f6e");
+
+ final static byte[] trunc224 = Hex.decode("f52bbcfd654264e7133085c5e69b72c3");
+ final static byte[] trunc256 = Hex.decode("745e7e687f8335280d54202ef13cecc6");
+ final static byte[] trunc384 = Hex.decode("fa9aea2bc1e181e47cbb8c3df243814d");
+ final static byte[] trunc512 = Hex.decode("04c929fead434bba190dacfa554ce3f5");
+
+ final static byte[] xtremeData = Hex.decode("61626364656667686263646566676869636465666768696a6465666768696a6b65666768696a6b6c666768696a6b6c6d6768696a6b6c6d6e68696a6b6c6d6e6f");
+
+ SHA3DigestTest()
+ {
+ }
+
+ public String getName()
+ {
+ return "SHA3";
+ }
+
+ private void testDigest(Digest digest, String[] expected)
+ {
+ byte[] hash = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i != messages.length; i++)
+ {
+ if (messages.length != 0)
+ {
+ byte[] data = Hex.decode(messages[i]);
+
+ digest.update(data, 0, data.length);
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[i]), hash))
+ {
+ fail("sha3 mismatch on " + digest.getAlgorithmName() + " index " + i);
+ }
+ }
+
+ byte[] k64 = new byte[1024 * 64];
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ k64[i] = (byte)'a';
+ }
+
+ digest.update(k64, 0, k64.length);
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length]), hash))
+ {
+ fail("sha3 mismatch on " + digest.getAlgorithmName() + " 64k a");
+ }
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ digest.update((byte)'a');
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length]), hash))
+ {
+ fail("sha3 mismatch on " + digest.getAlgorithmName() + " 64k a single");
+ }
+
+
+ for (int i = 0; i != k64.length; i++)
+ {
+ k64[i] = (byte)('a' + (i % 26));
+ }
+
+ digest.update(k64, 0, k64.length);
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length + 1]), hash))
+ {
+ fail("sha3 mismatch on " + digest.getAlgorithmName() + " 64k alpha");
+ }
+
+ for (int i = 0; i != 64; i++)
+ {
+ digest.update(k64[i * 1024]);
+ digest.update(k64, i * 1024 + 1, 1023);
+ }
+
+ digest.doFinal(hash, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[messages.length + 1]), hash))
+ {
+ fail("sha3 mismatch on " + digest.getAlgorithmName() + " 64k chunked alpha");
+ }
+
+ testDigestDoFinal(digest);
+
+ //
+ // extremely long data test
+ //
+// System.out.println("Starting very long");
+// for (int i = 0; i != 16384; i++)
+// {
+// for (int j = 0; j != 1024; j++)
+// {
+// digest.update(xtremeData, 0, xtremeData.length);
+// }
+// }
+//
+// digest.doFinal(hash, 0);
+//
+// if (!Arrays.areEqual(Hex.decode(expected[messages.length + 2]), hash))
+// {
+// fail("sha3 mismatch on " + digest.getAlgorithmName() + " extreme data test");
+// }
+// System.out.println("Done");
+ }
+
+ private void testDigestDoFinal(Digest digest)
+ {
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+
+ for (int i = 0; i <= digest.getDigestSize(); ++i)
+ {
+ byte[] cmp = new byte[2 * digest.getDigestSize()];
+ System.arraycopy(hash, 0, cmp, i, hash.length);
+
+ byte[] buf = new byte[2 * digest.getDigestSize()];
+ digest.doFinal(buf, i);
+
+ if (!Arrays.areEqual(cmp, buf))
+ {
+ fail("sha3 offset doFinal on " + digest.getAlgorithmName());
+ }
+ }
+ }
+
+ private void testMac(Digest digest, byte[][] keys, String[] data, String[] expected, byte[] truncExpected)
+ {
+ Mac mac = new HMac(digest);
+
+ for (int i = 0; i != keys.length; i++)
+ {
+ mac.init(new KeyParameter(keys[i]));
+
+ byte[] mData = Hex.decode(data[i]);
+
+ mac.update(mData, 0, mData.length);
+
+ byte[] macV = new byte[mac.getMacSize()];
+
+ mac.doFinal(macV, 0);
+
+ if (!Arrays.areEqual(Hex.decode(expected[i]), macV))
+ {
+ fail("sha3 HMAC mismatch on " + digest.getAlgorithmName());
+ }
+ }
+
+ mac = new HMac(digest);
+
+ mac.init(truncKey);
+
+ mac.update(truncData, 0, truncData.length);
+
+ byte[] macV = new byte[mac.getMacSize()];
+
+ mac.doFinal(macV, 0);
+
+ for (int i = 0; i != truncExpected.length; i++)
+ {
+ if (macV[i] != truncExpected[i])
+ {
+ fail("mismatch on truncated HMAC for " + digest.getAlgorithmName());
+ }
+ }
+ }
+
+ public void performTest()
+ {
+ testDigest(new SHA3Digest(), digests288);
+ testDigest(new SHA3Digest(224), digests224);
+ testDigest(new SHA3Digest(256), digests256);
+ testDigest(new SHA3Digest(384), digests384);
+ testDigest(new SHA3Digest(512), digests512);
+
+ testMac(new SHA3Digest(224), macKeys, macData, mac224, trunc224);
+ testMac(new SHA3Digest(256), macKeys, macData, mac256, trunc256);
+ testMac(new SHA3Digest(384), macKeys, macData, mac384, trunc384);
+ testMac(new SHA3Digest(512), macKeys, macData, mac512, trunc512);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA3Digest((SHA3Digest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA3DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512DigestTest.java
new file mode 100644
index 00000000..ffebaee4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512DigestTest.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+
+/**
+ * standard vector test for SHA-512 from FIPS Draft 180-2.
+ *
+ * Note, the first two vectors are _not_ from the draft, the last three are.
+ */
+public class SHA512DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+ };
+
+ private static String[] digests =
+ {
+ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
+ "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75",
+ "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+ "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b";
+
+ SHA512DigestTest()
+ {
+ super(new SHA512Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA512Digest((SHA512Digest)digest);
+ }
+
+ protected Digest cloneDigest(byte[] encodedState)
+ {
+ return new SHA512Digest(encodedState);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA512DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512HMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512HMacTest.java
new file mode 100644
index 00000000..ee163eb5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512HMacTest.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTestResult;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+/**
+ * SHA512 HMac Test
+ */
+public class SHA512HMacTest
+ implements Test
+{
+ final static String[] keys = {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "4a656665",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "0102030405060708090a0b0c0d0e0f10111213141516171819",
+ "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ };
+
+ final static String[] digests = {
+ "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
+ "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
+ "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb",
+ "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd",
+ "415fad6271580a531d4179bc891d87a650188707922a4fbb36663a1eb16da008711c5b50ddd0fc235084eb9d3364a1454fb2ef67cd1d29fe6773068ea266e96b",
+ "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598",
+ "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"
+ };
+
+ final static String[] messages = {
+ "Hi There",
+ "what do ya want for nothing?",
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
+ "Test With Truncation",
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm."
+ };
+
+ public String getName()
+ {
+ return "SHA512HMac";
+ }
+
+ public TestResult perform()
+ {
+ HMac hmac = new HMac(new SHA512Digest());
+ byte[] resBuf = new byte[hmac.getMacSize()];
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ byte[] m = messages[i].getBytes();
+ if (messages[i].startsWith("0x"))
+ {
+ m = Hex.decode(messages[i].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[i])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[i])))
+ {
+ return new SimpleTestResult(false, getName() + ": Vector " + i + " failed got -" + new String(Hex.encode(resBuf)));
+ }
+ }
+
+ //
+ // test reset
+ //
+ int vector = 0; // vector used for test
+ byte[] m = messages[vector].getBytes();
+ if (messages[vector].startsWith("0x"))
+ {
+ m = Hex.decode(messages[vector].substring(2));
+ }
+ hmac.init(new KeyParameter(Hex.decode(keys[vector])));
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+ hmac.reset();
+ hmac.update(m, 0, m.length);
+ hmac.doFinal(resBuf, 0);
+
+ if (!Arrays.areEqual(resBuf, Hex.decode(digests[vector])))
+ {
+ return new SimpleTestResult(false, getName() +
+ "Reset with vector " + vector + " failed");
+ }
+
+ return new SimpleTestResult(true, getName() + ": Okay");
+ }
+
+ public static void main(
+ String[] args)
+ {
+ SHA512HMacTest test = new SHA512HMacTest();
+ TestResult result = test.perform();
+
+ System.out.println(result);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512t224DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512t224DigestTest.java
new file mode 100644
index 00000000..0c7bc954
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512t224DigestTest.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA512tDigest;
+
+/**
+ * standard vector test for SHA-512/224 from FIPS 180-4.
+ *
+ * Note, only the last 2 message entries are FIPS originated..
+ */
+public class SHA512t224DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+ };
+
+ private static String[] digests =
+ {
+ "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4",
+ "d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327",
+ "4634270F707B6A54DAAE7530460842E20E37ED265CEEE9A43E8924AA",
+ "23FEC5BB94D60B23308192640B0C453335D664734FE40E7268674AF9"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287";
+
+ SHA512t224DigestTest()
+ {
+ super(new SHA512tDigest(224), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA512tDigest((SHA512tDigest)digest);
+ }
+
+ protected Digest cloneDigest(byte[] encodedState)
+ {
+ return new SHA512tDigest(encodedState);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA512t224DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512t256DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512t256DigestTest.java
new file mode 100644
index 00000000..db019ae6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHA512t256DigestTest.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA512tDigest;
+
+/**
+ * standard vector test for SHA-512/256 from FIPS 180-4.
+ *
+ * Note, only the last 2 message entries are FIPS originated..
+ */
+public class SHA512t256DigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+ };
+
+ private static String[] digests =
+ {
+ "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a",
+ "455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8",
+ "53048E2681941EF99B2E29B76B4C7DABE4C2D0C634FC6D46E0E2F13107E7AF23",
+ "3928E184FB8690F840DA3988121D31BE65CB9D3EF83EE6146FEAC861E19B563A"
+ };
+
+ // 1 million 'a'
+ static private String million_a_digest = "9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21";
+
+ SHA512t256DigestTest()
+ {
+ super(new SHA512tDigest(256), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SHA512tDigest((SHA512tDigest)digest);
+ }
+
+ protected Digest cloneDigest(byte[] encodedState)
+ {
+ return new SHA512tDigest(encodedState);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SHA512t256DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM3DigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM3DigestTest.java
new file mode 100644
index 00000000..2d418cb5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM3DigestTest.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SM3Digest;
+
+/**
+ * standard vector test for SM3 digest from chinese specification
+ */
+public class SM3DigestTest
+ extends DigestTest
+{
+ private static String[] messages = {
+ // Standard test vectors
+ "abc",
+ "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
+ // Non-standard test vectors
+ "",
+ "a",
+ "abcdefghijklmnopqrstuvwxyz",
+ };
+
+ private static String[] digests = {
+ // Standard test vectors
+ "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0",
+ "debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732",
+ // Non-standard test vectors
+ "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b",
+ "623476ac18f65a2909e43c7fec61b49c7e764a91a18ccb82f1917a29c86c5e88",
+ "b80fe97a4da24afc277564f66a359ef440462ad28dcc6d63adb24d5c20a61595",
+ };
+
+ final static String sixtyFourKdigest = "97049bdc8f0736bc7300eafa9980aeb9cf00f24f7ec3a8f1f8884954d7655c1d";
+ final static String million_a_digest = "c8aaf89429554029e231941a2acc0ad61ff2a5acd8fadd25847a3a732b3b02c3";
+
+ SM3DigestTest()
+ {
+ super(new SM3Digest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ sixtyFourKTest(sixtyFourKdigest);
+ millionATest(million_a_digest);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new SM3Digest((SM3Digest)digest);
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SM3DigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SRP6Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SRP6Test.java
new file mode 100644
index 00000000..2409faff
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SRP6Test.java
@@ -0,0 +1,267 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.agreement.srp.SRP6Client;
+import org.bouncycastle.crypto.agreement.srp.SRP6Server;
+import org.bouncycastle.crypto.agreement.srp.SRP6StandardGroups;
+import org.bouncycastle.crypto.agreement.srp.SRP6Util;
+import org.bouncycastle.crypto.agreement.srp.SRP6VerifierGenerator;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.generators.DHParametersGenerator;
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class SRP6Test extends SimpleTest
+{
+ private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+ private static BigInteger fromHex(String hex)
+ {
+ return new BigInteger(1, Hex.decode(hex));
+ }
+
+ private final SecureRandom random = new SecureRandom();
+
+ public String getName()
+ {
+ return "SRP6";
+ }
+
+ public void performTest() throws Exception
+ {
+ rfc5054AppendixBTestVectors();
+
+ testMutualVerification(SRP6StandardGroups.rfc5054_1024);
+ testClientCatchesBadB(SRP6StandardGroups.rfc5054_1024);
+ testServerCatchesBadA(SRP6StandardGroups.rfc5054_1024);
+
+ testWithRandomParams(256);
+ testWithRandomParams(384);
+ testWithRandomParams(512);
+ }
+
+ private void rfc5054AppendixBTestVectors() throws Exception
+ {
+ byte[] I = "alice".getBytes("UTF8");
+ byte[] P = "password123".getBytes("UTF8");
+ byte[] s = Hex.decode("BEB25379D1A8581EB5A727673A2441EE");
+ BigInteger N = SRP6StandardGroups.rfc5054_1024.getN();
+ BigInteger g = SRP6StandardGroups.rfc5054_1024.getG();
+ BigInteger a = fromHex("60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DDDA2D4393");
+ BigInteger b = fromHex("E487CB59D31AC550471E81F00F6928E01DDA08E974A004F49E61F5D105284D20");
+
+ BigInteger expect_k = fromHex("7556AA045AEF2CDD07ABAF0F665C3E818913186F");
+ BigInteger expect_x = fromHex("94B7555AABE9127CC58CCF4993DB6CF84D16C124");
+ BigInteger expect_v = fromHex("7E273DE8696FFC4F4E337D05B4B375BEB0DDE1569E8FA00A9886D812"
+ + "9BADA1F1822223CA1A605B530E379BA4729FDC59F105B4787E5186F5"
+ + "C671085A1447B52A48CF1970B4FB6F8400BBF4CEBFBB168152E08AB5"
+ + "EA53D15C1AFF87B2B9DA6E04E058AD51CC72BFC9033B564E26480D78"
+ + "E955A5E29E7AB245DB2BE315E2099AFB");
+ BigInteger expect_A = fromHex("61D5E490F6F1B79547B0704C436F523DD0E560F0C64115BB72557EC4"
+ + "4352E8903211C04692272D8B2D1A5358A2CF1B6E0BFCF99F921530EC"
+ + "8E39356179EAE45E42BA92AEACED825171E1E8B9AF6D9C03E1327F44"
+ + "BE087EF06530E69F66615261EEF54073CA11CF5858F0EDFDFE15EFEA"
+ + "B349EF5D76988A3672FAC47B0769447B");
+ BigInteger expect_B = fromHex("BD0C61512C692C0CB6D041FA01BB152D4916A1E77AF46AE105393011"
+ + "BAF38964DC46A0670DD125B95A981652236F99D9B681CBF87837EC99"
+ + "6C6DA04453728610D0C6DDB58B318885D7D82C7F8DEB75CE7BD4FBAA"
+ + "37089E6F9C6059F388838E7A00030B331EB76840910440B1B27AAEAE"
+ + "EB4012B7D7665238A8E3FB004B117B58");
+ BigInteger expect_u = fromHex("CE38B9593487DA98554ED47D70A7AE5F462EF019");
+ BigInteger expect_S = fromHex("B0DC82BABCF30674AE450C0287745E7990A3381F63B387AAF271A10D"
+ + "233861E359B48220F7C4693C9AE12B0A6F67809F0876E2D013800D6C"
+ + "41BB59B6D5979B5C00A172B4A2A5903A0BDCAF8A709585EB2AFAFA8F"
+ + "3499B200210DCC1F10EB33943CD67FC88A2F39A4BE5BEC4EC0A3212D"
+ + "C346D7E474B29EDE8A469FFECA686E5A");
+
+ BigInteger k = SRP6Util.calculateK(new SHA1Digest(), N, g);
+ if (!k.equals(expect_k))
+ {
+ fail("wrong value of 'k'");
+ }
+
+ BigInteger x = SRP6Util.calculateX(new SHA1Digest(), N, s, I, P);
+ if (!x.equals(expect_x))
+ {
+ fail("wrong value of 'x'");
+ }
+
+ SRP6VerifierGenerator gen = new SRP6VerifierGenerator();
+ gen.init(N, g, new SHA1Digest());
+ BigInteger v = gen.generateVerifier(s, I, P);
+ if (!v.equals(expect_v))
+ {
+ fail("wrong value of 'v'");
+ }
+
+ final BigInteger aVal = a;
+ SRP6Client client = new SRP6Client()
+ {
+ protected BigInteger selectPrivateValue()
+ {
+ return aVal;
+ }
+ };
+ client.init(N, g, new SHA1Digest(), random);
+
+ BigInteger A = client.generateClientCredentials(s, I, P);
+ if (!A.equals(expect_A))
+ {
+ fail("wrong value of 'A'");
+ }
+
+ final BigInteger bVal = b;
+ SRP6Server server = new SRP6Server()
+ {
+ protected BigInteger selectPrivateValue()
+ {
+ return bVal;
+ }
+ };
+ server.init(N, g, v, new SHA1Digest(), random);
+
+ BigInteger B = server.generateServerCredentials();
+ if (!B.equals(expect_B))
+ {
+ fail("wrong value of 'B'");
+ }
+
+ BigInteger u = SRP6Util.calculateU(new SHA1Digest(), N, A, B);
+ if (!u.equals(expect_u))
+ {
+ fail("wrong value of 'u'");
+ }
+
+ BigInteger clientS = client.calculateSecret(B);
+ if (!clientS.equals(expect_S))
+ {
+ fail("wrong value of 'S' (client)");
+ }
+
+ BigInteger serverS = server.calculateSecret(A);
+ if (!serverS.equals(expect_S))
+ {
+ fail("wrong value of 'S' (server)");
+ }
+ }
+
+ private void testWithRandomParams(int bits) throws CryptoException
+ {
+ DHParametersGenerator paramGen = new DHParametersGenerator();
+ paramGen.init(bits, 25, random);
+ DHParameters parameters = paramGen.generateParameters();
+
+ testMutualVerification(new SRP6GroupParameters(parameters.getP(), parameters.getG()));
+ }
+
+ private void testMutualVerification(SRP6GroupParameters group) throws CryptoException
+ {
+ byte[] I = "username".getBytes();
+ byte[] P = "password".getBytes();
+ byte[] s = new byte[16];
+ random.nextBytes(s);
+
+ SRP6VerifierGenerator gen = new SRP6VerifierGenerator();
+ gen.init(group, new SHA256Digest());
+ BigInteger v = gen.generateVerifier(s, I, P);
+
+ SRP6Client client = new SRP6Client();
+ client.init(group, new SHA256Digest(), random);
+
+ SRP6Server server = new SRP6Server();
+ server.init(group, v, new SHA256Digest(), random);
+
+ BigInteger A = client.generateClientCredentials(s, I, P);
+ BigInteger B = server.generateServerCredentials();
+
+ BigInteger clientS = client.calculateSecret(B);
+ BigInteger serverS = server.calculateSecret(A);
+
+ if (!clientS.equals(serverS))
+ {
+ fail("SRP agreement failed - client/server calculated different secrets");
+ }
+ }
+
+ private void testClientCatchesBadB(SRP6GroupParameters group)
+ {
+ byte[] I = "username".getBytes();
+ byte[] P = "password".getBytes();
+ byte[] s = new byte[16];
+ random.nextBytes(s);
+
+ SRP6Client client = new SRP6Client();
+ client.init(group, new SHA256Digest(), random);
+
+ client.generateClientCredentials(s, I, P);
+
+ try
+ {
+ client.calculateSecret(ZERO);
+ fail("Client failed to detect invalid value for 'B'");
+ }
+ catch (CryptoException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ client.calculateSecret(group.getN());
+ fail("Client failed to detect invalid value for 'B'");
+ }
+ catch (CryptoException e)
+ {
+ // Expected
+ }
+ }
+
+ private void testServerCatchesBadA(SRP6GroupParameters group)
+ {
+ byte[] I = "username".getBytes();
+ byte[] P = "password".getBytes();
+ byte[] s = new byte[16];
+ random.nextBytes(s);
+
+ SRP6VerifierGenerator gen = new SRP6VerifierGenerator();
+ gen.init(group, new SHA256Digest());
+ BigInteger v = gen.generateVerifier(s, I, P);
+
+ SRP6Server server = new SRP6Server();
+ server.init(group, v, new SHA256Digest(), random);
+
+ server.generateServerCredentials();
+
+ try
+ {
+ server.calculateSecret(ZERO);
+ fail("Client failed to detect invalid value for 'A'");
+ }
+ catch (CryptoException e)
+ {
+ // Expected
+ }
+
+ try
+ {
+ server.calculateSecret(group.getN());
+ fail("Client failed to detect invalid value for 'A'");
+ }
+ catch (CryptoException e)
+ {
+ // Expected
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SRP6Test());
+ }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Salsa20Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Salsa20Test.java
new file mode 100644
index 00000000..2b738181
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Salsa20Test.java
@@ -0,0 +1,400 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.Salsa20Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Salsa20 Test
+ */
+public class Salsa20Test
+ extends SimpleTest
+{
+ byte[] zeroes = Hex.decode(
+ "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000"
+ + "00000000000000000000000000000000");
+
+ String set1v0_0 = "4DFA5E481DA23EA09A31022050859936"
+ + "DA52FCEE218005164F267CB65F5CFD7F"
+ + "2B4F97E0FF16924A52DF269515110A07"
+ + "F9E460BC65EF95DA58F740B7D1DBB0AA";
+
+ String set1v0_192 = "DA9C1581F429E0A00F7D67E23B730676"
+ + "783B262E8EB43A25F55FB90B3E753AEF"
+ + "8C6713EC66C51881111593CCB3E8CB8F"
+ + "8DE124080501EEEB389C4BCB6977CF95";
+
+ String set1v0_256 = "7D5789631EB4554400E1E025935DFA7B"
+ + "3E9039D61BDC58A8697D36815BF1985C"
+ + "EFDF7AE112E5BB81E37ECF0616CE7147"
+ + "FC08A93A367E08631F23C03B00A8DA2F";
+
+ String set1v0_448 = "B375703739DACED4DD4059FD71C3C47F"
+ + "C2F9939670FAD4A46066ADCC6A564578"
+ + "3308B90FFB72BE04A6B147CBE38CC0C3"
+ + "B9267C296A92A7C69873F9F263BE9703";
+
+ String set1v9_0 = "0471076057830FB99202291177FBFE5D"
+ + "38C888944DF8917CAB82788B91B53D1C"
+ + "FB06D07A304B18BB763F888A61BB6B75"
+ + "5CD58BEC9C4CFB7569CB91862E79C459";
+
+ String set1v9_192 = "D1D7E97556426E6CFC21312AE3811425"
+ + "9E5A6FB10DACBD88E4354B0472556935"
+ + "2B6DA5ACAFACD5E266F9575C2ED8E6F2"
+ + "EFE4B4D36114C3A623DD49F4794F865B";
+
+ String set1v9_256 = "AF06FAA82C73291231E1BD916A773DE1"
+ + "52FD2126C40A10C3A6EB40F22834B8CC"
+ + "68BD5C6DBD7FC1EC8F34165C517C0B63"
+ + "9DB0C60506D3606906B8463AA0D0EC2F";
+
+ String set1v9_448 = "AB3216F1216379EFD5EC589510B8FD35"
+ + "014D0AA0B613040BAE63ECAB90A9AF79"
+ + "661F8DA2F853A5204B0F8E72E9D9EB4D"
+ + "BA5A4690E73A4D25F61EE7295215140C";
+
+ String set6v0_0 = "F5FAD53F79F9DF58C4AEA0D0ED9A9601"
+ + "F278112CA7180D565B420A48019670EA"
+ + "F24CE493A86263F677B46ACE1924773D"
+ + "2BB25571E1AA8593758FC382B1280B71";
+
+ String set6v0_65472 = "B70C50139C63332EF6E77AC54338A407"
+ + "9B82BEC9F9A403DFEA821B83F7860791"
+ + "650EF1B2489D0590B1DE772EEDA4E3BC"
+ + "D60FA7CE9CD623D9D2FD5758B8653E70";
+
+ String set6v0_65536 = "81582C65D7562B80AEC2F1A673A9D01C"
+ + "9F892A23D4919F6AB47B9154E08E699B"
+ + "4117D7C666477B60F8391481682F5D95"
+ + "D96623DBC489D88DAA6956B9F0646B6E";
+
+ String set6v1_0 = "3944F6DC9F85B128083879FDF190F7DE"
+ + "E4053A07BC09896D51D0690BD4DA4AC1"
+ + "062F1E47D3D0716F80A9B4D85E6D6085"
+ + "EE06947601C85F1A27A2F76E45A6AA87";
+
+ String set6v1_65472 = "36E03B4B54B0B2E04D069E690082C8C5"
+ + "92DF56E633F5D8C7682A02A65ECD1371"
+ + "8CA4352AACCB0DA20ED6BBBA62E177F2"
+ + "10E3560E63BB822C4158CAA806A88C82";
+
+ String set6v1_65536 = "1B779E7A917C8C26039FFB23CF0EF8E0"
+ + "8A1A13B43ACDD9402CF5DF38501098DF"
+ + "C945A6CC69A6A17367BC03431A86B3ED"
+ + "04B0245B56379BF997E25800AD837D7D";
+
+ // Salsa20/12
+ String salsa12_set1v0_0 = "FC207DBFC76C5E1774961E7A5AAD0906"
+ + "9B2225AC1CE0FE7A0CE77003E7E5BDF8"
+ + "B31AF821000813E6C56B8C1771D6EE70"
+ + "39B2FBD0A68E8AD70A3944B677937897";
+
+ String salsa12_set1v0_192 = "4B62A4881FA1AF9560586510D5527ED4"
+ + "8A51ECAFA4DECEEBBDDC10E9918D44AB"
+ + "26B10C0A31ED242F146C72940C6E9C37"
+ + "53F641DA84E9F68B4F9E76B6C48CA5AC";
+
+ String salsa12_set1v0_256 = "F52383D9DEFB20810325F7AEC9EADE34"
+ + "D9D883FEE37E05F74BF40875B2D0BE79"
+ + "ED8886E5BFF556CEA8D1D9E86B1F68A9"
+ + "64598C34F177F8163E271B8D2FEB5996";
+
+ String salsa12_set1v0_448 = "A52ED8C37014B10EC0AA8E05B5CEEE12"
+ + "3A1017557FB3B15C53E6C5EA8300BF74"
+ + "264A73B5315DC821AD2CAB0F3BB2F152"
+ + "BDAEA3AEE97BA04B8E72A7B40DCC6BA4";
+
+ // Salsa20/8
+ String salsa8_set1v0_0 = "A9C9F888AB552A2D1BBFF9F36BEBEB33"
+ + "7A8B4B107C75B63BAE26CB9A235BBA9D"
+ + "784F38BEFC3ADF4CD3E266687EA7B9F0"
+ + "9BA650AE81EAC6063AE31FF12218DDC5";
+
+ String salsa8_set1v0_192 = "BB5B6BB2CC8B8A0222DCCC1753ED4AEB"
+ + "23377ACCBD5D4C0B69A8A03BB115EF71"
+ + "871BC10559080ACA7C68F0DEF32A80DD"
+ + "BAF497259BB76A3853A7183B51CC4B9F";
+
+ String salsa8_set1v0_256 = "4436CDC0BE39559F5E5A6B79FBDB2CAE"
+ + "4782910F27FFC2391E05CFC78D601AD8"
+ + "CD7D87B074169361D997D1BED9729C0D"
+ + "EB23418E0646B7997C06AA84E7640CE3";
+
+ String salsa8_set1v0_448 = "BEE85903BEA506B05FC04795836FAAAC"
+ + "7F93F785D473EB762576D96B4A65FFE4"
+ + "63B34AAE696777FC6351B67C3753B89B"
+ + "A6B197BD655D1D9CA86E067F4D770220";
+
+
+ public String getName()
+ {
+ return "Salsa20";
+ }
+
+ public void performTest()
+ {
+ salsa20Test1(20, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ set1v0_0, set1v0_192, set1v0_256, set1v0_448);
+ salsa20Test1(20, new ParametersWithIV(new KeyParameter(Hex.decode("00400000000000000000000000000000")), Hex.decode("0000000000000000")),
+ set1v9_0, set1v9_192, set1v9_256, set1v9_448);
+ salsa20Test1(12, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ salsa12_set1v0_0, salsa12_set1v0_192, salsa12_set1v0_256, salsa12_set1v0_448);
+ salsa20Test1(8, new ParametersWithIV(new KeyParameter(Hex.decode("80000000000000000000000000000000")), Hex.decode("0000000000000000")),
+ salsa8_set1v0_0, salsa8_set1v0_192, salsa8_set1v0_256, salsa8_set1v0_448);
+ salsa20Test2(new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE")),
+ set6v0_0, set6v0_65472, set6v0_65536);
+ salsa20Test2(new ParametersWithIV(new KeyParameter(Hex.decode("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12")), Hex.decode("167DE44BB21980E7")),
+ set6v1_0, set6v1_65472, set6v1_65536);
+ reinitBug();
+ skipTest();
+ }
+
+ private void salsa20Test1(int rounds, CipherParameters params, String v0, String v192, String v256, String v448)
+ {
+ StreamCipher salsa = new Salsa20Engine(rounds);
+ byte[] buf = new byte[64];
+
+ salsa.init(true, params);
+
+ for (int i = 0; i != 7; i++)
+ {
+ salsa.processBytes(zeroes, 0, 64, buf, 0);
+ switch (i)
+ {
+ case 0:
+ if (!areEqual(buf, Hex.decode(v0)))
+ {
+ mismatch("v0/" + rounds, v0, buf);
+ }
+ break;
+ case 3:
+ if (!areEqual(buf, Hex.decode(v192)))
+ {
+ mismatch("v192/" + rounds, v192, buf);
+ }
+ break;
+ case 4:
+ if (!areEqual(buf, Hex.decode(v256)))
+ {
+ mismatch("v256/" + rounds, v256, buf);
+ }
+ break;
+ default:
+ // ignore
+ }
+ }
+
+ for (int i = 0; i != 64; i++)
+ {
+ buf[i] = salsa.returnByte(zeroes[i]);
+ }
+
+ if (!areEqual(buf, Hex.decode(v448)))
+ {
+ mismatch("v448", v448, buf);
+ }
+ }
+
+ private void salsa20Test2(CipherParameters params, String v0, String v65472, String v65536)
+ {
+ StreamCipher salsa = new Salsa20Engine();
+ byte[] buf = new byte[64];
+
+ salsa.init(true, params);
+
+ for (int i = 0; i != 1025; i++)
+ {
+ salsa.processBytes(zeroes, 0, 64, buf, 0);
+ switch (i)
+ {
+ case 0:
+ if (!areEqual(buf, Hex.decode(v0)))
+ {
+ mismatch("v0", v0, buf);
+ }
+ break;
+ case 1023:
+ if (!areEqual(buf, Hex.decode(v65472)))
+ {
+ mismatch("v65472", v65472, buf);
+ }
+ break;
+ case 1024:
+ if (!areEqual(buf, Hex.decode(v65536)))
+ {
+ mismatch("v65536", v65536, buf);
+ }
+ break;
+ default:
+ // ignore
+ }
+ }
+ }
+
+ private void mismatch(String name, String expected, byte[] found)
+ {
+ fail("mismatch on " + name, expected, new String(Hex.encode(found)));
+ }
+
+
+ private void reinitBug()
+ {
+ KeyParameter key = new KeyParameter(Hex.decode("80000000000000000000000000000000"));
+ ParametersWithIV parameters = new ParametersWithIV(key, Hex.decode("0000000000000000"));
+
+ StreamCipher salsa = new Salsa20Engine();
+
+ salsa.init(true, parameters);
+
+ try
+ {
+ salsa.init(true, key);
+ fail("Salsa20 should throw exception if no IV in Init");
+ }
+ catch (IllegalArgumentException e)
+ {
+ }
+ }
+
+ private boolean areEqual(byte[] a, int aOff, byte[] b, int bOff)
+ {
+ for (int i = bOff; i != b.length; i++)
+ {
+ if (a[aOff + i - bOff] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void skipTest()
+ {
+ SecureRandom rand = new SecureRandom();
+ byte[] plain = new byte[50000];
+ byte[] cipher = new byte[50000];
+
+ rand.nextBytes(plain);
+
+ CipherParameters params = new ParametersWithIV(new KeyParameter(Hex.decode("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D")), Hex.decode("0D74DB42A91077DE"));
+ Salsa20Engine engine = new Salsa20Engine();
+
+ engine.init(true, params);
+
+ engine.processBytes(plain, 0, plain.length, cipher, 0);
+
+ byte[] fragment = new byte[20];
+
+ engine.init(true, params);
+
+ engine.skip(10);
+
+ engine.processBytes(plain, 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 10, fragment, 0))
+ {
+ fail("skip forward 10 failed");
+ }
+
+ engine.skip(1000);
+
+ engine.processBytes(plain, 1010 + fragment.length, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + fragment.length, fragment, 0))
+ {
+ fail("skip forward 1000 failed");
+ }
+
+ engine.skip(-10);
+
+ engine.processBytes(plain, 1010 + 2 * fragment.length - 10, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010 + 2 * fragment.length - 10, fragment, 0))
+ {
+ fail("skip back 10 failed");
+ }
+
+ engine.skip(-1000);
+
+ if (engine.getPosition() != 60)
+ {
+ fail("skip position incorrect - " + 60 + " got " + engine.getPosition());
+ }
+
+ engine.processBytes(plain, 60, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 60, fragment, 0))
+ {
+ fail("skip back 1000 failed");
+ }
+
+ long pos = engine.seekTo(1010);
+ if (pos != 1010)
+ {
+ fail("position wrong");
+ }
+
+ engine.processBytes(plain, 1010, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, 1010, fragment, 0))
+ {
+ fail("seek to 1010 failed");
+ }
+
+ engine.reset();
+
+ for (int i = 0; i != 5000; i++)
+ {
+ engine.skip(i);
+
+ if (engine.getPosition() != i)
+ {
+ fail("skip forward at wrong position");
+ }
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip forward i failed: " + i);
+ }
+
+ if (engine.getPosition() != i + fragment.length)
+ {
+ fail("cipher at wrong position: " + engine.getPosition() + " [" + i + "]");
+ }
+
+ engine.skip(-fragment.length);
+
+ if (engine.getPosition() != i)
+ {
+ fail("skip back at wrong position");
+ }
+
+ engine.processBytes(plain, i, fragment.length, fragment, 0);
+
+ if (!areEqual(cipher, i, fragment, 0))
+ {
+ fail("skip back i failed: " + i);
+ }
+
+ engine.reset();
+ }
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Salsa20Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SerpentTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SerpentTest.java
new file mode 100644
index 00000000..3eac33a7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SerpentTest.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.SerpentEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ */
+public class SerpentTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000000", "8910494504181950f98dd998a82b6749"),
+ new BlockCipherVectorTest(1, new SerpentEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "80000000000000000000000000000000", "10b5ffb720b8cb9002a1142b0ba2e94a"),
+ new BlockCipherVectorTest(2, new SerpentEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000008000000000000000000000", "4f057a42d8d5bd9746e434680ddcd5e5"),
+ new BlockCipherVectorTest(3, new SerpentEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "00000000000000000000400000000000", "99407bf8582ef12550886ef5b6f169b9"),
+ new BlockCipherVectorTest(4, new SerpentEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "40000000000000000000000000000000", "d522a3b8d6d89d4d2a124fdd88f36896"),
+ new BlockCipherVectorTest(5, new SerpentEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00000000000200000000000000000000", "189b8ec3470085b3da97e82ca8964e32"),
+ new BlockCipherVectorTest(6, new SerpentEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")),
+ "00000000000000000000008000000000", "f77d868cf760b9143a89809510ccb099"),
+ new BlockCipherVectorTest(7, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "08000000000000000000000000000000", "d43b7b981b829342fce0e3ec6f5f4c82"),
+ new BlockCipherVectorTest(8, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000100000000000000", "0bf30e1a0c33ccf6d5293177886912a7"),
+ new BlockCipherVectorTest(9, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")),
+ "00000000000000000000000000000001", "6a7f3b805d2ddcba49b89770ade5e507"),
+ new BlockCipherVectorTest(10, new SerpentEngine(),
+ new KeyParameter(Hex.decode("80000000000000000000000000000000")),
+ "00000000000000000000000000000000", "49afbfad9d5a34052cd8ffa5986bd2dd"),
+ new BlockCipherVectorTest(11, new SerpentEngine(),
+ new KeyParameter(Hex.decode("000000000000000000000000004000000000000000000000")),
+ "00000000000000000000000000000000", "ba8829b1de058c4b48615d851fc74f17"),
+ new BlockCipherVectorTest(12, new SerpentEngine(),
+ new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000100000000")),
+ "00000000000000000000000000000000", "89f64377bf1e8a46c8247044e8056a98"),
+/*
+ new BlockCipherMonteCarloTest(13, 10000, new SerpentEngine(),
+ new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")),
+ "7a4f7db38c52a8b711b778a38d203b6b", "003380e19f10065740394f48e2fe80b7"),
+*/
+ new BlockCipherMonteCarloTest(13, 100, new SerpentEngine(),
+ new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")),
+ "7a4f7db38c52a8b711b778a38d203b6b", "4db75303d815c2f7cc6ca935d1c5a046"),
+/*
+ new BlockCipherMonteCarloTest(14, 10000, new SerpentEngine(),
+ new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")),
+ "70a05e12f74589009692a337f53ff614", "afb5425426906db26b70bdf842ac5400"),
+*/
+ new BlockCipherMonteCarloTest(14, 100, new SerpentEngine(),
+ new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")),
+ "70a05e12f74589009692a337f53ff614", "fc53a50f4d3bc9836001893d2f41742d"),
+/*
+ new BlockCipherMonteCarloTest(15, 10000, new SerpentEngine(),
+ new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")),
+ "9cc523d034a93740a0aa4e2054bb34d8", "1949d506ada7de1f1344986e8ea049b2"),
+*/
+ new BlockCipherMonteCarloTest(15, 100, new SerpentEngine(),
+ new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")),
+ "9cc523d034a93740a0aa4e2054bb34d8", "77117e6a9e80f40b2a36b7d755573c2d"),
+/*
+ new BlockCipherMonteCarloTest(16, 10000, new SerpentEngine(),
+ new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")),
+ "ee1a61106fae2d381d686cbf854bab65", "e57f45559027cb1f2ed9603d814e1c34"),
+*/
+ new BlockCipherMonteCarloTest(16, 100, new SerpentEngine(),
+ new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")),
+ "ee1a61106fae2d381d686cbf854bab65", "dcd7f13ea0dcdfd0139d1a42e2ffb84b")
+ };
+
+ SerpentTest()
+ {
+ super(tests, new SerpentEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Serpent";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SerpentTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Shacal2Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Shacal2Test.java
new file mode 100644
index 00000000..bd7a8154
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Shacal2Test.java
@@ -0,0 +1,200 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.Shacal2Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Shacal2 tester - vectors from https://www.cosic.esat.kuleuven.be/nessie/testvectors/
+ */
+public class Shacal2Test
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ // set 8.0
+ new BlockCipherVectorTest(0, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("000102030405060708090A0B0C0D0E0F" +
+ "101112131415161718191A1B1C1D1E1F" +
+ "202122232425262728292A2B2C2D2E2F" +
+ "303132333435363738393A3B3C3D3E3F")),
+ "98BCC10405AB0BFC686BECECAAD01AC1" +
+ "9B452511BCEB9CB094F905C51CA45430",
+ "00112233445566778899AABBCCDDEEFF" +
+ "102132435465768798A9BACBDCEDFE0F"),
+ // set 8.1
+ new BlockCipherVectorTest(1, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("2BD6459F82C5B300952C49104881FF48" +
+ "2BD6459F82C5B300952C49104881FF48" +
+ "2BD6459F82C5B300952C49104881FF48" +
+ "2BD6459F82C5B300952C49104881FF48")),
+ "481F122A75F2C4C3395140B5A951EBBA" +
+ "06D96BDFD9D8FF4FB59CBD1287808D5A",
+ "EA024714AD5C4D84EA024714AD5C4D84" +
+ "EA024714AD5C4D84EA024714AD5C4D84"),
+ // 7.255
+ new BlockCipherVectorTest(2, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")),
+ "94FEDFF2A0CFE3C983D340C88D73F8CF" +
+ "4B79FC581797EC10B27D4DA1B51E1BC7",
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
+ // 7.100
+ new BlockCipherVectorTest(3, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("64646464646464646464646464646464" +
+ "64646464646464646464646464646464" +
+ "64646464646464646464646464646464" +
+ "64646464646464646464646464646464")),
+ "6643CB84B3B3F126F5E50959EF4CE73D" +
+ "B8500918ABE1056368DB06CA8C1C0D45",
+ "64646464646464646464646464646464" +
+ "64646464646464646464646464646464"),
+ // 7.50
+ new BlockCipherVectorTest(4, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("32323232323232323232323232323232" +
+ "32323232323232323232323232323232" +
+ "32323232323232323232323232323232" +
+ "32323232323232323232323232323232")),
+ "92E937285AB11FE3561542C43C918966" +
+ "971DE722E9B9D38BD69EAC77899DCF81",
+ "32323232323232323232323232323232" +
+ "32323232323232323232323232323232"),
+ // 7.0
+ new BlockCipherVectorTest(5, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000")),
+ "F8C9259FA4F5D787B570AFA9219166A6" +
+ "3636FC5C30AC289155D0CC4FFCB4B03D",
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ // 6.255
+ new BlockCipherVectorTest(6, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000")),
+ "F4E976DF0172CD961D4C8D466A12F676" +
+ "5B9089046E747CD2A41BF43C18A8328E",
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000001"),
+ // 6.100
+ new BlockCipherVectorTest(7, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000")),
+ "3B929F0597E21D0076EC399D21B67713" +
+ "B40E3AD559704219A26A3380212D5AD6",
+ "00000000000000000000000008000000" +
+ "00000000000000000000000000000000"),
+
+ // 6.0
+ new BlockCipherVectorTest(8, new Shacal2Engine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000")),
+ "43A0DAD8307F19FBBCF166FE20BAC075" +
+ "C56FF14042550E472094B042BE5963EE",
+ "80000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ };
+
+ Shacal2Test()
+ {
+ super(tests, new Shacal2Engine(), new KeyParameter(new byte[16]));
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ super.performTest();
+
+ // 1.0
+ iteratedTest(0,
+ Hex.decode("80000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ Hex.decode("361AB6322FA9E7A7BB23818D839E01BD" +
+ "DAFDF47305426EDD297AEDB9F6202BAE"),
+ Hex.decode("226A582DE04383D0F3E7DE655DD848AC" +
+ "3E14CCFB4E76F7B7069879F67C4D5420"),
+ Hex.decode("B05D5A18C0712082CFF5BA9DBBCD7269" +
+ "114FC3DF83B42DAC306D95BBC473D839"));
+
+ // 1.100
+ iteratedTest(1,
+ Hex.decode("00000000000000000000000008000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ Hex.decode("00000000000000000000000000000000" +
+ "00000000000000000000000000000000"),
+ Hex.decode("F703282E54592A5617E10618027BB67F" +
+ "639E43A90767150D8B7F5E83054B3CBD"),
+ Hex.decode("3B442692B579485B8BA2F92CE3B90DE7" +
+ "D2EA03D8B3C8E7BE7BF6415F798EED90"),
+ Hex.decode("331B9B65F06230380BBEECFBFBA94BCF" +
+ "92AF6341F815D7651F996144A5377263"));
+ }
+
+ private void iteratedTest(int index, byte[] key, byte[] plain, byte[] cipher, byte[] cipher100, byte[] cipher1000)
+ {
+ BlockCipher engine = new Shacal2Engine();
+
+ engine.init(true, new KeyParameter(key));
+
+ byte[] buf = new byte[plain.length];
+
+ System.arraycopy(plain, 0, buf, 0, plain.length);
+
+ engine.processBlock(buf, 0, buf, 0);
+
+ if (!Arrays.areEqual(cipher, buf))
+ {
+ fail(index + " single count failed");
+ }
+
+ for (int i = 1; i != 100; i++)
+ {
+ engine.processBlock(buf, 0, buf, 0);
+ }
+
+ if (!Arrays.areEqual(cipher100, buf))
+ {
+ fail(index + " 100 count failed");
+ }
+
+ for (int i = 100; i != 1000; i++)
+ {
+ engine.processBlock(buf, 0, buf, 0);
+ }
+
+ if (!Arrays.areEqual(cipher1000, buf))
+ {
+ fail(index + " 1000 count failed");
+ }
+ }
+
+ public String getName()
+ {
+ return "Shacal2";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Shacal2Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/ShortenedDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/ShortenedDigestTest.java
new file mode 100644
index 00000000..20510c94
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/ShortenedDigestTest.java
@@ -0,0 +1,89 @@
+/*
+ * Created on 6/05/2006
+ *
+ * To change the template for this generated file go to
+ * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
+ */
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.ShortenedDigest;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class ShortenedDigestTest
+ extends SimpleTest
+{
+ public void performTest()
+ {
+ ExtendedDigest d = new SHA1Digest();
+ ShortenedDigest sd = new ShortenedDigest(new SHA1Digest(), 10);
+
+ if (sd.getDigestSize() != 10)
+ {
+ fail("size check wrong for SHA-1");
+ }
+
+ if (sd.getByteLength() != d.getByteLength())
+ {
+ fail("byte length check wrong for SHA-1");
+ }
+
+ //
+ // check output fits
+ //
+ sd.doFinal(new byte[10], 0);
+
+ d = new SHA512Digest();
+ sd = new ShortenedDigest(new SHA512Digest(), 20);
+
+ if (sd.getDigestSize() != 20)
+ {
+ fail("size check wrong for SHA-512");
+ }
+
+ if (sd.getByteLength() != d.getByteLength())
+ {
+ fail("byte length check wrong for SHA-512");
+ }
+
+ //
+ // check output fits
+ //
+ sd.doFinal(new byte[20], 0);
+
+ try
+ {
+ new ShortenedDigest(null, 20);
+
+ fail("null parameter not caught");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+
+ try
+ {
+ new ShortenedDigest(new SHA1Digest(), 50);
+
+ fail("short digest not caught");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // expected
+ }
+ }
+
+ public String getName()
+ {
+ return "ShortenedDigest";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new ShortenedDigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SipHashTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SipHashTest.java
new file mode 100644
index 00000000..1c55411d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SipHashTest.java
@@ -0,0 +1,143 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.macs.SipHash;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Pack;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/*
+ * SipHash test values from "SipHash: a fast short-input PRF", by Jean-Philippe
+ * Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf), Appendix A.
+ */
+public class SipHashTest
+ extends SimpleTest
+{
+ private static final int UPDATE_BYTES = 0;
+ private static final int UPDATE_FULL = 1;
+ private static final int UPDATE_MIX = 2;
+
+ public String getName()
+ {
+ return "SipHash";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ byte[] key = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] input = Hex.decode("000102030405060708090a0b0c0d0e");
+
+ runMAC(key, input, UPDATE_BYTES);
+ runMAC(key, input, UPDATE_FULL);
+ runMAC(key, input, UPDATE_MIX);
+
+ SecureRandom random = new SecureRandom();
+ for (int i = 0; i < 100; ++i)
+ {
+ randomTest(random);
+ }
+ }
+
+ private void runMAC(byte[] key, byte[] input, int updateType)
+ throws Exception
+ {
+ long expected = 0xa129ca6149be45e5L;
+
+ SipHash mac = new SipHash();
+ mac.init(new KeyParameter(key));
+
+ updateMAC(mac, input, updateType);
+
+ long result = mac.doFinal();
+ if (expected != result)
+ {
+ fail("Result does not match expected value for doFinal()");
+ }
+
+ byte[] expectedBytes = new byte[8];
+ Pack.longToLittleEndian(expected, expectedBytes, 0);
+
+ updateMAC(mac, input, updateType);
+
+ byte[] output = new byte[mac.getMacSize()];
+ int len = mac.doFinal(output, 0);
+ if (len != output.length)
+ {
+ fail("Result length does not equal getMacSize() for doFinal(byte[],int)");
+ }
+ if (!areEqual(expectedBytes, output))
+ {
+ fail("Result does not match expected value for doFinal(byte[],int)");
+ }
+ }
+
+ private void randomTest(SecureRandom random)
+ {
+ byte[] key = new byte[16];
+ random.nextBytes(key);
+
+ int length = 1 + random.nextInt(1024);
+ byte[] input = new byte[length];
+ random.nextBytes(input);
+
+ SipHash mac = new SipHash();
+ mac.init(new KeyParameter(key));
+
+ updateMAC(mac, input, UPDATE_BYTES);
+ long result1 = mac.doFinal();
+
+ updateMAC(mac, input, UPDATE_FULL);
+ long result2 = mac.doFinal();
+
+ updateMAC(mac, input, UPDATE_MIX);
+ long result3 = mac.doFinal();
+
+ if (result1 != result2 || result1 != result3)
+ {
+ fail("Inconsistent results in random test");
+ }
+ }
+
+ private void updateMAC(SipHash mac, byte[] input, int updateType)
+ {
+ switch (updateType)
+ {
+ case UPDATE_BYTES:
+ {
+ for (int i = 0; i < input.length; ++i)
+ {
+ mac.update(input[i]);
+ }
+ break;
+ }
+ case UPDATE_FULL:
+ {
+ mac.update(input, 0, input.length);
+ break;
+ }
+ case UPDATE_MIX:
+ {
+ int step = Math.max(1, input.length / 3);
+ int pos = 0;
+ while (pos < input.length)
+ {
+ mac.update(input[pos++]);
+ int len = Math.min(input.length - pos, step);
+ mac.update(input, pos, len);
+ pos += len;
+ }
+ break;
+ }
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new SipHashTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SkeinDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SkeinDigestTest.java
new file mode 100644
index 00000000..1a7dff6f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SkeinDigestTest.java
@@ -0,0 +1,294 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SkeinDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class SkeinDigestTest
+ extends SimpleTest
+{
+ private static class Case
+ {
+ private byte[] message;
+ private byte[] digest;
+ private int blockSize;
+ private int outputSize;
+
+ public Case(int blockSize, int outputSize, String message, String digest)
+ {
+ this.blockSize = blockSize;
+ this.outputSize = outputSize;
+ this.message = Hex.decode(message);
+ this.digest = Hex.decode(digest);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSize;
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public byte[] getMessage()
+ {
+ return message;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ }
+
+ // Test cases from skein_golden_kat.txt and skein_golden_kat_short.txt in Skein 1.3 NIST CD
+ private static final Case[] TEST_CASES = {
+ new Case(256, 256, "", "c8877087da56e072870daa843f176e9453115929094c3a40c463a196c29bf7ba"),
+ new Case(256, 256, "fb", "088eb23cc2bccfb8171aa64e966d4af937325167dfcd170700ffd21f8a4cbdac"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "5c3002ff57a627089ea2f97a5000d5678416389019e80e45a3bbcab118315d26"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "640c894a4bba6574c83e920ddf7dd2982fc634881bbbcb9d774eae0a285e89ce"),
+ new Case(256, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "0cd491b7715704c3a15a45a1ca8d93f8f646d3a1"),
+ new Case(256, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "afd1e2d0f5b6cd4e1f8b3935fa2497d27ee97e72060adac099543487"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "4de6fe2bfdaa3717a4261030ef0e044ced9225d066354610842a24a3eafd1dcf"),
+ new Case(256, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "954620fb31e8b782a2794c6542827026fe069d715df04261629fcbe81d7d529b"
+ + "95ba021fa4239fb00afaa75f5fd8e78b"),
+ new Case(256, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "51347e27c7eabba514959f899a6715ef6ad5cf01c23170590e6a8af399470bf9"
+ + "0ea7409960a708c1dbaa90e86389df254abc763639bb8cdf7fb663b29d9557c3"),
+ new Case(256, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "6c9b6facbaf116b538aa655e0be0168084aa9f1be445f7e06714585e5999a6c9"
+ + "84fffa9d41a316028692d4aad18f573fbf27cf78e84de26da1928382b023987d"
+ + "cfe002b6201ea33713c54a8a5d9eb346f0365e04330d2faaf7bc8aba92a5d7fb"
+ + "6345c6fb26750bce65ab2045c233627679ac6e9acb33602e26fe3526063ecc8b"),
+
+ new Case(512, 512, "", "bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af4"
+ + "1fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a"),
+ new Case(512, 512, "fb", "c49e03d50b4b2cc46bd3b7ef7014c8a45b016399fd1714467b7596c86de98240"
+ + "e35bf7f9772b7d65465cd4cffab14e6bc154c54fc67b8bc340abf08eff572b9e"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "abefb179d52f68f86941acbbe014cc67ec66ad78b7ba9508eb1400ee2cbdb06f"
+ + "9fe7c2a260a0272d0d80e8ef5e8737c0c6a5f1c02ceb00fb2746f664b85fcef5"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "5c5b7956f9d973c0989aa40a71aa9c48a65af2757590e9a758343c7e23ea2df4"
+ + "057ce0b49f9514987feff97f648e1dd065926e2c371a0211ca977c213f14149f"),
+ new Case(512, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "ef03079d61b57c6047e15fa2b35b46fa24279539"),
+ new Case(512, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "d9e3219b214e15246a2038f76a573e018ef69b385b3bd0576b558231"),
+ new Case(512, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "809dd3f763a11af90912bbb92bc0d94361cbadab10142992000c88b4ceb88648"),
+ new Case(512, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "825f5cbd5da8807a7b4d3e7bd9cd089ca3a256bcc064cd73a9355bf3ae67f2bf"
+ + "93ac7074b3b19907a0665ba3a878b262"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "1a0d5abf4432e7c612d658f8dcfa35b0d1ab68b8d6bd4dd115c23cc57b5c5bcd"
+ + "de9bff0ece4208596e499f211bc07594d0cb6f3c12b0e110174b2a9b4b2cb6a9"),
+
+ new Case(1024, 1024, "", "0fff9563bb3279289227ac77d319b6fff8d7e9f09da1247b72a0a265cd6d2a62"
+ + "645ad547ed8193db48cff847c06494a03f55666d3b47eb4c20456c9373c86297"
+ + "d630d5578ebd34cb40991578f9f52b18003efa35d3da6553ff35db91b81ab890"
+ + "bec1b189b7f52cb2a783ebb7d823d725b0b4a71f6824e88f68f982eefc6d19c6"),
+ new Case(1024, 1024, "fb", "6426bdc57b2771a6ef1b0dd39f8096a9a07554565743ac3de851d28258fcff22"
+ + "9993e11c4e6bebc8b6ecb0ad1b140276081aa390ec3875960336119427827473"
+ + "4770671b79f076771e2cfdaaf5adc9b10cbae43d8e6cd2b1c1f5d6c82dc96618"
+ + "00ddc476f25865b8748253173187d81da971c027d91d32fb390301c2110d2db2"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "140e93726ab0b0467c0b8a834ad8cda4d1769d273661902b70db0dcb5ee692ac"
+ + "b3f852d03b11f857850f2428432811309c1dcbe5724f00267ea3667e89fadb4e"
+ + "4911da6b0ba8a7eddf87c1c67152ef0f07b7fead3557318478bdef5ad1e5926d"
+ + "7071fdd4bfa5076d4b3253f8de479ebdf5357676f1641b2f097e9b785e9e528e"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "31105e1ef042c30b95b16e0f6e6a1a19172bb7d54a0597dd0c711194888efe1d"
+ + "bce82d47416df9577ca387219f06e45cd10964ff36f6711edbbea0e9595b0f66"
+ + "f72b755d70a46857e0aec98561a743d49370d8e572e212811273125f66cc30bf"
+ + "117d3221894c48012bf6e2219de91e064b01523517420a1e00f71c4cc04bab62"),
+ new Case(1024, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "2e6a4cbf2ef05ea9c24b93e8d1de732ddf2739eb"),
+ new Case(1024, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "1d6de19f37f7a3c265440eecb4b9fbd3300bb5ac60895cfc0d4d3c72"),
+ new Case(1024, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "986a4d472b123e8148731a8eac9db23325f0058c4ccbc44a5bb6fe3a8db672d7"),
+ new Case(1024, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "9c3d0648c11f31c18395d5e6c8ebd73f43d189843fc45235e2c35e345e12d62b"
+ + "c21a41f65896ddc6a04969654c2e2ce9"),
+ new Case(1024, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "5d0416f49c2d08dfd40a1446169dc6a1d516e23b8b853be4933513051de8d5c2"
+ + "6baccffb08d3b16516ba3c6ccf3e9a6c78fff6ef955f2dbc56e1459a7cdba9a5"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "96ca81f586c825d0360aef5acaec49ad55289e1797072eee198b64f349ce65b6"
+ + "e6ed804fe38f05135fe769cc56240ddda5098f620865ce4a4278c77fa2ec6bc3"
+ + "1c0f354ca78c7ca81665bfcc5dc54258c3b8310ed421d9157f36c093814d9b25"
+ + "103d83e0ddd89c52d0050e13a64c6140e6388431961685734b1f138fe2243086"),
+
+ };
+
+ public String getName()
+ {
+ return "SkeinDigest";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ runTest(TEST_CASES[7]);
+ for (int i = 0; i < TEST_CASES.length; i++)
+ {
+ Case test = TEST_CASES[i];
+ runTest(test);
+ }
+ }
+
+ private void runTest(Case dc)
+ {
+ SkeinDigest digest = new SkeinDigest(dc.getBlockSize(), dc.getOutputSize());
+
+ byte[] message = dc.getMessage();
+ digest.update(message, 0, message.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ if (!Arrays.areEqual(output, dc.getDigest()))
+ {
+ fail(digest.getAlgorithmName() + " message mismatch.\n Message " + new String(Hex.encode(dc.getMessage())),
+ new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ // Clone test
+ digest.update(message, 0, message.length / 2);
+
+ // clone the Digest
+ Digest d = new SkeinDigest(digest);
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing clone vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ d.update(message, message.length / 2, message.length - message.length / 2);
+ d.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing second clone vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ //
+ // memo test
+ //
+ Memoable m = (Memoable)digest;
+
+ digest.update(message, 0, message.length / 2);
+
+ // copy the Digest
+ Memoable copy1 = m.copy();
+ Memoable copy2 = copy1.copy();
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ m.reset(copy1);
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo reset vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ Digest md = (Digest)copy2;
+
+ md.update(message, message.length / 2, message.length - message.length / 2);
+ md.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo copy vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+ }
+
+ public static void main(String[] args)
+ throws IOException
+ {
+ // generateTests();
+ runTest(new SkeinDigestTest());
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SkeinMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SkeinMacTest.java
new file mode 100644
index 00000000..68008b7a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SkeinMacTest.java
@@ -0,0 +1,162 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class SkeinMacTest
+ extends SimpleTest
+{
+ private static class Case
+ {
+ private byte[] message;
+ private byte[] digest;
+ private byte[] key;
+ private int blockSize;
+ private int outputSize;
+
+ public Case(int blockSize, int outputSize, String message, String key, String digest)
+ {
+ this.blockSize = blockSize;
+ this.outputSize = outputSize;
+ this.message = Hex.decode(message);
+ this.key = Hex.decode(key);
+ this.digest = Hex.decode(digest);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSize;
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public byte[] getMessage()
+ {
+ return message;
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ public String toString()
+ {
+ return "new Case(" + blockSize + ", " + outputSize + ", \"" + new String(Hex.encode(message)) + "\", \""
+ + new String(Hex.encode(key)) + "\", \"" + new String(Hex.encode(digest)) + "\"";
+ }
+
+ }
+
+ // Test cases from skein_golden_kat.txt in Skein 1.3 NIST CD
+ // Excludes empty '(none)' key 'random+MAC' tests, which are in effect digest
+ private static final Case[] TEST_CASES = {
+ new Case(256, 256, "", "cb41f1706cde09651203c2d0efbaddf8", "886e4efefc15f06aa298963971d7a25398fffe5681c84db39bd00851f64ae29d"),
+ new Case(256, 256, "d3", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "979422a94e3afaa46664124d4e5e8b9422b1d8baf11c6ae6725992ac72a112ca"),
+ new Case(256, 256, "d3090c72", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "1d658372cbea2f9928493cc47599d6f4ad8ce33536bedfa20b739f07516519d5"),
+ new Case(256, 256, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "41ef6b0f0fad81c040284f3b1a91e9c44e4c26a6d7207f3aac4362856ef12aca"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "ca8208119b9e4e4057631ab31015cfd256f6763a0a34381633d97f640899b84f"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "9e9980fcc16ee082cf164a5147d0e0692aeffe3dcb8d620e2bb542091162e2e9"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc235", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "c353a316558ec34f8245dd2f9c2c4961fbc7decc3b69053c103e4b8aaaf20394"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf8", "b1b8c18188e69a6ecae0b6018e6b638c6a91e6de6881e32a60858468c17b520d"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "1dfd2515a412e78852cd81a7f2167711b4ca19b2891c2ea36ba94f8451944793"),
+ new Case(256, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf8", "a097340709b443ed2c0a921f5dcefef3ead65c4f0bcd5f13da54d7ed"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "ac1b4fab6561c92d0c487e082daec53e0db4f505e08bf51cae4fd5375e37fc04"),
+ new Case(256, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "96e6cebb23573d0a70ce36a67aa05d2403148093f25c695e1254887cc97f9771d2518413af4286bf2a06b61a53f7fcec"),
+ new Case(256, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "0e95e597e71d6350f20b99c4179f54f43a4722705c06ba765a82cb0a314fe2fe87ef8090063b757e53182706ed18737dadc0da1e1c66518f08334052702c5ed7"),
+ new Case(256, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf8", "064abd4896f460b1953f5a357e7f7c5256e29cdb62b8740d0b52295cfa2ef4c7a2"),
+ new Case(256, 520, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "edf220e43e048603bd16197d59b673b9974de5b8bcf7cb1558a4799f6fd3743eb5fb400cd6129afc0c60e7b741b7e5806f0e0b93eb8429fbc7efa222175a9c80fd"),
+ new Case(256, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "f3f59fb07399c7b73aae02a8590883cb2fdfde75c55654e71846522301bde48d267169adcc559e038e8c2f28faa552b550d51874055384adea93c036c71a1f0af0c7bcc3bc923738d5307b9da7cb423d4e615c629c4aba71f70d4c9d1fa008176825e51bfa0203445a4083947ec19f6a0fbd082b5b970f2396fb67420639410447"),
+ new Case(256, 2056, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "80eb80d9b8836b32fa576fc84ba08edfbdfd6979123d61914e610a70a372b37f560a10909484f9f4a377c93e29ba681dfe522c41dc83b5ee0567e5370007c7bbe4df0b2b4a25e088f80d72fc30734cdcd76d817b42fbd44dca881019afb25306f19d4e91848778af306517d2072cef72caa327e877c5b6554f83cec3d00877131b47c4d3b557f5a13541c4d5080ee3ce7a658993d083efd0db3496a8752060c3c8552f44b290cabdcc867f691ad605836c08dbd59c9528d885b600b85fdfc8a9d0e636ac3ad8b4295bcb0169e78dc358e77eacc8c4b61bddfa9e5f32d2268a006cfe05c57150fe8e68cabd21cf6cf6035aa1fe4db36c922b765aad0b64e82a2c37"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed696e6c9db1e6abea026288954a9c2d5758d7c5db7c9e48aa3d21cae3d977a7c3926066aa393dbd538dd0c30da8916c8757f24c18488014668a2627163a37b261833dc2f8c3c56b1b2e0be21fd3fbdb507b2950b77a6cc02efb393e57419383a920767bca2c972107aa61384542d47cbfb82cfe5c415389d1b0a2d74e2c5da851", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "8f88de68f03cd2f396ccdd49c3a0f4ff15bcda7eb357da9753f6116b124de91d"),
+ new Case(512, 512, "", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "9bd43d2a2fcfa92becb9f69faab3936978f1b865b7e44338fc9c8f16aba949ba340291082834a1fc5aa81649e13d50cd98641a1d0883062bfe2c16d1faa7e3aa"),
+ new Case(512, 512, "d3", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "f0c0a10f031c8fc69cfabcd54154c318b5d6cd95d06b12cf20264402492211ee010d5cecc2dc37fd772afac0596b2bf71e6020ef2dee7c860628b6e643ed9ff6"),
+ new Case(512, 512, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "0c1f1921253dd8e5c2d4c5f4099f851042d91147892705829161f5fc64d89785226eb6e187068493ee4c78a4b7c0f55a8cbbb1a5982c2daf638fc6a74b16b0d7"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "478d7b6c0cc6e35d9ebbdedf39128e5a36585db6222891692d1747d401de34ce3db6fcbab6c968b7f2620f4a844a2903b547775579993736d2493a75ff6752a1"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e59", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "13c170bac1de35e5fb843f65fabecf214a54a6e0458a4ff6ea5df91915468f4efcd371effa8965a9e82c5388d84730490dcf3976af157b8baf550655a5a6ab78"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc235", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "a947812529a72fd3b8967ec391b298bee891babc8487a1ec4ea3d88f6b2b5be09ac6a780f30f8e8c3bbb4f18bc302a28f3e87d170ba0f858a8fefe3487478cca"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "7690ba61f10e0bba312980b0212e6a9a51b0e9aadfde7ca535754a706e042335b29172aae29d8bad18efaf92d43e6406f3098e253f41f2931eda5911dc740352"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "d10e3ba81855ac087fbf5a3bc1f99b27d05f98ba22441138026225d34a418b93fd9e8dfaf5120757451adabe050d0eb59d271b0fe1bbf04badbcf9ba25a8791b"),
+ new Case(512, 160, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "5670b226156570dff3efe16661ab86eb24982cdf"),
+ new Case(512, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "c41b9ff9753e6c0f8ed88866e320535e927fe4da552c289841a920db"),
+ new Case(512, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "dfbf5c1319a1d9d70efb2f1600fbcf694f935907f31d24a16d6cd2fb2d7855a769681766c0a29da778eed346cd1d740f"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "04d8cddb0ad931d54d195899a094684344e902286037272890bce98a41813edc37a3cee190a693fcca613ee30049ce7ec2bdff9613f56778a13f8c28a21d167a"),
+ new Case(512, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "08fca368b3b14ac406676adf37ac9be2dbb8704e694055a0c6331184d4f0070098f23f0963ee29002495771bf56fb4d3d9ff3506abcd80be927379f7880d5d7703919fbf92184f498ac44f47f015ce676eded9165d47d53733f5a27abbc05f45acd98b97cc15ffdced641defd1a5119ef841b452a1b8f94ee69004466ccdc143"),
+ new Case(512, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "669e770ebe7eacc2b64caaf049923ad297a5b37cfa61c283392d81ccfcb9bbbc09"),
+ new Case(512, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "acc2e03f07f33e9820a6038421089429adcd6a7a83f733beec048c05bf37531a170a5537fcb565c348a70a83217f8be768ff6f95fd2b3d89cb7d8a3dc849505e3710eb4e65a8e7134bbf580d92fe18c9aa987563669b1f014aa5e092519089355534eaa9f0bdc99f6839f54080ffe74623254c906ecb8896b4346c3178a0bc2898"),
+ new Case(512, 2056, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "9f3e082223c43090a4a3ffbdcd469cbabfe0c1399d1edf45a5dfc18f4db5428928a76e979b8d0d5dffec0e6a59ada448c1ffbc06cc80a2006f002adc0c6dbf458563762228dce4381944e460ebebfe06f1237093634625107469a22a189a47f8b025899265d8890a1b39df64552394377e88ba2ad44a8c8d174f884ac8c3ae24ddb0affca5fceb6aa76e09706881e8371774b9b050a69b96ef5e97e81043f8b7e9479e287ab441bacd62caf768a82c8c3e3107be70eb8799a39856fe29842a04e25de0ef9de1b7e65bd0f1f7306835287fc957388e2035b7d22d3aa9c06a9fefbca16f3f60e1c4def89038d918942152a069aa2e0be8ae7475d859031adec84583"),
+ new Case(1024, 1024, "", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "bcf37b3459c88959d6b6b58b2bfe142cef60c6f4ec56b0702480d7893a2b0595aa354e87102a788b61996b9cbc1eade7dafbf6581135572c09666d844c90f066b800fc4f5fd1737644894ef7d588afc5c38f5d920bdbd3b738aea3a3267d161ed65284d1f57da73b68817e17e381ca169115152b869c66b812bb9a84275303f0"),
+ new Case(1024, 1024, "d3090c72", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "df0596e5808835a3e304aa27923db05f61dac57c0696a1d19abf188e70aa9dbcc659e9510f7c9a37fbc025bd4e5ea293e78ed7838dd0b08864e8ad40ddb3a88031ebefc21572a89960d1916107a7da7ac0c067e34ec46a86a29ca63fa250bd398eb32ec1ed0f8ac8329f26da018b029e41e2e58d1dfc44de81615e6c987ed9c9"),
+ new Case(1024, 1024, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "3cfbb79cd88af8ee09c7670bcbab6907a31f80fa31d9d7c9d50826c9568f307a78bd254961398c76b6e338fd9ca5f351059350d30963c3320659b223b991fc46d1307686fe2b4763d9f593c57ad5adbc45caf2ea3dc6090f5a74fa5fa6d9e9838964ea0a2aa216831ab069b00629a1a9b037083403bdb25d3d06a21c430c87dd"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e59", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "0a1b960099fc9d653b0fd1f5b6b972fb366907b772cbce5a59b6171d7935506f70c212bd169d68c5cfd8618343611b7eb2e686ff1dc7c03a57e1a55ed10726848161eea903d53b58459be42d95df989c66c2eea4e51cde272c2d8be67bf3bca2aee633777eb8486781eaa060d0f538abd6c93dbd2d1bf66e6f50bfdcac3725a4"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "3e0cd7938d71c39ffbb08a6ba7995ade3ad140e2c0c45cdbafb099247e08e4c20b61c1f885ced5ed2f816680925034918236e5807f0eecf3f27e9cfca36675eb75873efa1fb41f17541dc2f7c2469eaecb35cc7ca58e489804caf56f09fb97c9f689c64ad49c6888f86c483e901bd3d25798b394ef93faf9154900f92f31f433"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "7266752f7e9aa04bd7d8a1b16030677de6021301f6a62473c76bae2b98bbf8aad73bd00a4b5035f741caf2317ab80e4e97f5c5bbe8acc0e8b424bcb13c7c6740a985801fba54addde8d4f13f69d2bfc98ae104d46a211145217e51d510ea846cec9581d14fda079f775c8b18d66cb31bf7060996ee8a69eee7f107909ce59a97"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "71f40bf2aa635125ef83c8df0d4e9ea18b73b56be4f45e89b910a7c68d396b65b09d18abc7d1b6de3f53fd5de583e6f22e612dd17b292068af6027daaf8b4cd60acf5bc85044741e9f7a1f423f5827f5e360930a2e71912239af9fc6343604fdcf3f3569854f2bb8d25a81e3b3f5261a02fe8292aaaa50c324101ab2c7a2f349"),
+ new Case(1024, 160, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "17c3c533b27d666da556ae586e641b7a3a0bcc45"),
+ new Case(1024, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "6625df9801581009125ea4e5c94ad6f1a2d692c278822ccb6eb67235"),
+ new Case(1024, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "6c5b671c1766f6eecea6d24b641d4a6bf84bba13a1976f8f80b3f30ee2f93de6"),
+ new Case(1024, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "98af454d7fa3706dfaafbf58c3f9944868b57f68f493987347a69fce19865febba0407a16b4e82065035651f0b1e0327"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "211ac479e9961141da3aac19d320a1dbbbfad55d2dce87e6a345fcd58e36827597378432b482d89bad44dddb13e6ad86e0ee1e0882b4eb0cd6a181e9685e18dd302ebb3aa74502c06254dcadfb2bd45d288f82366b7afc3bc0f6b1a3c2e8f84d37fbedd07a3f8fcff84faf24c53c11da600aaa118e76cfdcb366d0b3f7729dce"),
+ new Case(1024, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "dc1d253b7cadbdaef18503b1809a7f1d4f8c323b7f6f8ca50b76d3864649ce1c7d"),
+ new Case(1024, 520, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "decd79578d12bf6806530c382230a2c7836429c70cac941179e1dd982938bab91fb6f3638df1cc1ef615ecfc4249e5aca8a73c4c1eebef662a836d0be903b00146"),
+ new Case(1024, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "440fe691e04f1fed8c253d6c4670646156f33fffaea702de9445df5739eb960cecf85d56e2e6860a610211a5c909932ab774b978aa0b0d5bbce82775172ab12dceddd51d1eb030057ce61bea6c18f6bb368d26ae76a9e44a962eb132e6c42c25d9fecc4f13348300ca55c78e0990de96c1ae24eb3ee3324782c93dd628260a2c8d"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed696e6c9db1e6abea026288954a9c2d5758d7c5db7c9e48aa3d21cae3d977a7c3926066aa393dbd538dd0c30da8916c8757f24c18488014668a2627163a37b261833dc2f8c3c56b1b2e0be21fd3fbdb507b2950b77a6cc02efb393e57419383a920767bca2c972107aa61384542d47cbfb82cfe5c415389d1b0a2d74e2c5da851", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "46a42b0d7b8679f8fcea156c072cf9833c468a7d59ac5e5d326957d60dfe1cdfb27eb54c760b9e049fda47f0b847ac68d6b340c02c39d4a18c1bdfece3f405fae8aa848bdbefe3a4c277a095e921228618d3be8bd1999a071682810de748440ad416a97742cc9e8a9b85455b1d76472cf562f525116698d5cd0a35ddf86e7f8a"),
+
+ };
+
+ public String getName()
+ {
+ return "SkeinMac";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ for (int i = 0; i < TEST_CASES.length; i++)
+ {
+ Case test = TEST_CASES[i];
+ runTest(test);
+ }
+ }
+
+ private void runTest(Case dc)
+ {
+ Mac digest = new SkeinMac(dc.getBlockSize(), dc.getOutputSize());
+ digest.init(new KeyParameter(dc.getKey()));
+
+ byte[] message = dc.getMessage();
+ digest.update(message, 0, message.length);
+
+ byte[] output = new byte[digest.getMacSize()];
+ digest.doFinal(output, 0);
+
+ if (!Arrays.areEqual(output, dc.getDigest()))
+ {
+ fail(digest.getAlgorithmName() + " message " + (dc.getMessage().length * 8) + " mismatch.\n Message " + new String(Hex.encode(dc.getMessage()))
+ + "\n Key " + new String(Hex.encode(dc.getKey())) + "\n Expected "
+ + new String(Hex.encode(dc.getDigest())) + "\n Actual " + new String(Hex.encode(output)));
+ }
+
+ }
+
+ public static void main(String[] args)
+ throws IOException
+ {
+ runTest(new SkeinMacTest());
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SkipjackTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SkipjackTest.java
new file mode 100644
index 00000000..455e10bd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SkipjackTest.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.SkipjackEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ */
+public class SkipjackTest
+ extends CipherTest
+{
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new SkipjackEngine(),
+ new KeyParameter(Hex.decode("00998877665544332211")),
+ "33221100ddccbbaa", "2587cae27a12d300")
+ };
+
+ SkipjackTest()
+ {
+ super(tests, new SkipjackEngine(), new KeyParameter(Hex.decode("00998877665544332211")));
+ }
+
+ public String getName()
+ {
+ return "SKIPJACK";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new SkipjackTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/StreamCipherResetTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/StreamCipherResetTest.java
new file mode 100644
index 00000000..092de5fb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/StreamCipherResetTest.java
@@ -0,0 +1,133 @@
+package org.bouncycastle.crypto.test;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.engines.ChaChaEngine;
+import org.bouncycastle.crypto.engines.Grain128Engine;
+import org.bouncycastle.crypto.engines.Grainv1Engine;
+import org.bouncycastle.crypto.engines.HC128Engine;
+import org.bouncycastle.crypto.engines.HC256Engine;
+import org.bouncycastle.crypto.engines.ISAACEngine;
+import org.bouncycastle.crypto.engines.RC4Engine;
+import org.bouncycastle.crypto.engines.Salsa20Engine;
+import org.bouncycastle.crypto.engines.XSalsa20Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Test whether block ciphers implement reset contract on init, encrypt/decrypt and reset.
+ */
+public class StreamCipherResetTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "Stream Cipher Reset";
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ testReset(new Salsa20Engine(), new Salsa20Engine(), new ParametersWithIV(new KeyParameter(random(32)),
+ random(8)));
+ testReset(new Salsa20Engine(), new Salsa20Engine(), new ParametersWithIV(new KeyParameter(random(16)),
+ random(8)));
+ testReset(new XSalsa20Engine(), new XSalsa20Engine(), new ParametersWithIV(new KeyParameter(random(32)),
+ random(24)));
+ testReset(new ChaChaEngine(), new ChaChaEngine(), new ParametersWithIV(new KeyParameter(random(32)), random(8)));
+ testReset(new ChaChaEngine(), new ChaChaEngine(), new ParametersWithIV(new KeyParameter(random(16)), random(8)));
+ testReset(new RC4Engine(), new RC4Engine(), new KeyParameter(random(16)));
+ testReset(new ISAACEngine(), new ISAACEngine(), new KeyParameter(random(16)));
+ testReset(new HC128Engine(), new HC128Engine(), new ParametersWithIV(new KeyParameter(random(16)), random(16)));
+ testReset(new HC256Engine(), new HC256Engine(), new ParametersWithIV(new KeyParameter(random(16)), random(16)));
+ testReset(new Grainv1Engine(), new Grainv1Engine(), new ParametersWithIV(new KeyParameter(random(16)),
+ random(8)));
+ testReset(new Grain128Engine(), new Grain128Engine(), new ParametersWithIV(new KeyParameter(random(16)),
+ random(12)));
+ }
+
+ private static final SecureRandom RAND = new SecureRandom();
+
+ private byte[] random(int size)
+ {
+ final byte[] data = new byte[size];
+ RAND.nextBytes(data);
+ return data;
+ }
+
+ private void testReset(StreamCipher cipher1, StreamCipher cipher2, CipherParameters params)
+ throws InvalidCipherTextException
+ {
+ cipher1.init(true, params);
+
+ byte[] plaintext = new byte[1023];
+ byte[] ciphertext = new byte[plaintext.length];
+
+ // Establish baseline answer
+ cipher1.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
+
+ // Test encryption resets
+ checkReset(cipher1, params, true, plaintext, ciphertext);
+
+ // Test decryption resets with fresh instance
+ cipher2.init(false, params);
+ checkReset(cipher2, params, false, ciphertext, plaintext);
+ }
+
+ private void checkReset(StreamCipher cipher,
+ CipherParameters params,
+ boolean encrypt,
+ byte[] pretext,
+ byte[] posttext)
+ throws InvalidCipherTextException
+ {
+ // Do initial run
+ byte[] output = new byte[posttext.length];
+ cipher.processBytes(pretext, 0, pretext.length, output, 0);
+
+ // Check encrypt resets cipher
+ cipher.init(encrypt, params);
+
+ try
+ {
+ cipher.processBytes(pretext, 0, pretext.length, output, 0);
+ }
+ catch (Exception e)
+ {
+ fail(cipher.getAlgorithmName() + " init did not reset: " + e.getMessage());
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(cipher.getAlgorithmName() + " init did not reset.", new String(Hex.encode(posttext)),
+ new String(Hex.encode(output)));
+ }
+
+ // Check reset resets data
+ cipher.reset();
+
+ try
+ {
+ cipher.processBytes(pretext, 0, pretext.length, output, 0);
+ }
+ catch (Exception e)
+ {
+ fail(cipher.getAlgorithmName() + " reset did not reset: " + e.getMessage());
+ }
+ if (!Arrays.areEqual(output, posttext))
+ {
+ fail(cipher.getAlgorithmName() + " reset did not reset.");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new StreamCipherResetTest());
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/StreamCipherVectorTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/StreamCipherVectorTest.java
new file mode 100644
index 00000000..7b788280
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/StreamCipherVectorTest.java
@@ -0,0 +1,62 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * a basic test that takes a stream cipher, key parameter, and an input
+ * and output string.
+ */
+public class StreamCipherVectorTest
+ extends SimpleTest
+{
+ int id;
+ StreamCipher cipher;
+ CipherParameters param;
+ byte[] input;
+ byte[] output;
+
+ public StreamCipherVectorTest(
+ int id,
+ StreamCipher cipher,
+ CipherParameters param,
+ String input,
+ String output)
+ {
+ this.id = id;
+ this.cipher = cipher;
+ this.param = param;
+ this.input = Hex.decode(input);
+ this.output = Hex.decode(output);
+ }
+
+ public String getName()
+ {
+ return cipher.getAlgorithmName() + " Vector Test " + id;
+ }
+
+ public void performTest()
+ {
+ cipher.init(true, param);
+
+ byte[] out = new byte[input.length];
+
+ cipher.processBytes(input, 0, input.length, out, 0);
+
+ if (!areEqual(out, output))
+ {
+ fail("failed.", new String(Hex.encode(output)) , new String(Hex.encode(out)));
+ }
+
+ cipher.init(false, param);
+
+ cipher.processBytes(output, 0, output.length, out, 0);
+
+ if (!areEqual(input, out))
+ {
+ fail("failed reversal");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/TEATest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/TEATest.java
new file mode 100644
index 00000000..98b0ec9a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/TEATest.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.TEAEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * TEA tester - based on C implementation results from http://www.simonshepherd.supanet.com/tea.htm
+ */
+public class TEATest
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new TEAEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "0000000000000000",
+ "41ea3a0a94baa940"),
+ new BlockCipherVectorTest(1, new TEAEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "0102030405060708",
+ "6a2f9cf3fccf3c55"),
+ new BlockCipherVectorTest(2, new TEAEngine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0000000000000000",
+ "34e943b0900f5dcb"),
+ new BlockCipherVectorTest(3, new TEAEngine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0102030405060708",
+ "773dc179878a81c0"),
+ };
+
+ TEATest()
+ {
+ super(tests, new TEAEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "TEA";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TEATest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish1024Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish1024Test.java
new file mode 100644
index 00000000..a580a156
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish1024Test.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Threefish1024Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[128]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "f05c3d0a3d05b304f785ddc7d1e036015c8aa76e2f217b06c6e1544c0bc1a90d" +
+ "f0accb9473c24e0fd54fea68057f43329cb454761d6df5cf7b2e9b3614fbd5a2" +
+ "0b2e4760b40603540d82eabc5482c171c832afbe68406bc39500367a592943fa" +
+ "9a5b4a43286ca3c4cf46104b443143d560a4b230488311df4feef7e1dfe8391e"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f" +
+ "303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f" +
+ "505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f" +
+ "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0" +
+ "dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0" +
+ "bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a0" +
+ "9f9e9d9c9b9a999897969594939291908f8e8d8c8b8a89888786858483828180",
+ "a6654ddbd73cc3b05dd777105aa849bce49372eaaffc5568d254771bab85531c" +
+ "94f780e7ffaae430d5d8af8c70eebbe1760f3b42b737a89cb363490d670314bd" +
+ "8aa41ee63c2e1f45fbd477922f8360b388d6125ea6c7af0ad7056d01796e90c8" +
+ "3313f4150a5716b30ed5f569288ae974ce2b4347926fce57de44512177dd7cde")
+ };
+
+ Threefish1024Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024), new KeyParameter(new byte[128]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-1024";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish1024Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish256Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish256Test.java
new file mode 100644
index 00000000..10bd6080
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish256Test.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Threefish256Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[32]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "84da2a1f8beaee947066ae3e3103f1ad536db1f4a1192495116b9f3ce6133fd8"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0",
+ "e0d091ff0eea8fdfc98192e62ed80ad59d865d08588df476657056b5955e97df")
+ };
+
+ Threefish256Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-256";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish256Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish512Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish512Test.java
new file mode 100644
index 00000000..1eb4e85c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Threefish512Test.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Threefish512Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[64]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b" +
+ "7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f" +
+ "303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0" +
+ "dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0",
+ "e304439626d45a2cb401cad8d636249a6338330eb06d45dd8b36b90e97254779" +
+ "272a0a8d99463504784420ea18c9a725af11dffea10162348927673d5c1caf3d")
+ };
+
+ Threefish512Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512), new KeyParameter(new byte[64]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-512";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish512Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/TigerDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/TigerDigestTest.java
new file mode 100644
index 00000000..deb838f0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/TigerDigestTest.java
@@ -0,0 +1,59 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.TigerDigest;
+
+/**
+ * Tiger Digest Test
+ */
+public class TigerDigestTest
+ extends DigestTest
+{
+ final static String[] messages = {
+ "",
+ "abc",
+ "Tiger",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789",
+ "Tiger - A Fast New Hash Function, by Ross Anderson and Eli Biham, proceedings of Fast Software Encryption 3, Cambridge, 1996.",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"
+ };
+
+ final static String[] digests = {
+ "3293AC630C13F0245F92BBB1766E16167A4E58492DDE73F3",
+ "2AAB1484E8C158F2BFB8C5FF41B57A525129131C957B5F93",
+ "DD00230799F5009FEC6DEBC838BB6A27DF2B9D6F110C7937",
+ "F71C8583902AFB879EDFE610F82C0D4786A3A534504486B5",
+ "38F41D9D9A710A10C3727AC0DEEAA270727D9F926EC10139",
+ "48CEEB6308B87D46E95D656112CDF18D97915F9765658957",
+ "631ABDD103EB9A3D245B6DFD4D77B257FC7439501D1568DD",
+ "C54034E5B43EB8005848A7E0AE6AAC76E4FF590AE715FD25",
+ "C54034E5B43EB8005848A7E0AE6AAC76E4FF590AE715FD25"
+ };
+
+ final static String hash64k = "FDF4F5B35139F48E710E421BE5AF411DE1A8AAC333F26204";
+
+ TigerDigestTest()
+ {
+ super(new TigerDigest(), messages, digests);
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ sixtyFourKTest(hash64k);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new TigerDigest((TigerDigest)digest);
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TigerDigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/TwofishTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/TwofishTest.java
new file mode 100644
index 00000000..50a41c81
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/TwofishTest.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class TwofishTest
+ extends CipherTest
+{
+ static String key1 = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
+ static String key2 = "000102030405060708090a0b0c0d0e0f1011121314151617";
+ static String key3 = "000102030405060708090a0b0c0d0e0f";
+
+ static String input = "000102030405060708090A0B0C0D0E0F";
+
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new TwofishEngine(),
+ new KeyParameter(Hex.decode(key1)),
+ input, "8ef0272c42db838bcf7b07af0ec30f38"),
+ new BlockCipherVectorTest(1, new TwofishEngine(),
+ new KeyParameter(Hex.decode(key2)),
+ input, "95accc625366547617f8be4373d10cd7"),
+ new BlockCipherVectorTest(2, new TwofishEngine(),
+ new KeyParameter(Hex.decode(key3)),
+ input, "9fb63337151be9c71306d159ea7afaa4")
+ };
+
+ TwofishTest()
+ {
+ super(tests, new TwofishEngine(), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Twofish";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new TwofishTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCKSA3Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCKSA3Test.java
new file mode 100644
index 00000000..65fdfc22
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCKSA3Test.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.engines.VMPCKSA3Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * VMPC Test
+ */
+public class VMPCKSA3Test extends SimpleTest
+{
+ private static final byte[] input = new byte[1000000];
+
+ public String getName()
+ {
+ return "VMPC-KSA3";
+ }
+
+ private void checkByte(byte[] array, int position, byte b)
+ {
+ if (array[position] != b)
+ {
+ fail("Fail on position " + position,
+ new String(Hex.encode(new byte[] { b })),
+ new String(Hex.encode(new byte[] { array[position] })));
+ }
+ }
+
+ public void performTest()
+ {
+ byte[] key = Hex.decode("9661410AB797D8A9EB767C21172DF6C7");
+ byte[] iv = Hex.decode("4B5C2F003E67F39557A8D26F3DA2B155");
+ CipherParameters kp = new KeyParameter(key);
+ CipherParameters kpwiv = new ParametersWithIV(kp, iv);
+
+ VMPCKSA3Engine engine = new VMPCKSA3Engine();
+
+ try
+ {
+ engine.init(true, kp);
+ fail("init failed to throw expected exception");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ engine.init(true, kpwiv);
+ checkEngine(engine);
+
+ engine.reset();
+ byte[] output = checkEngine(engine);
+
+ engine.init(false, kpwiv);
+ byte[] recovered = new byte[output.length];
+ engine.processBytes(output, 0, output.length, recovered, 0);
+
+ if (!Arrays.areEqual(input, recovered))
+ {
+ fail("decrypted bytes differ from original bytes");
+ }
+ }
+
+ private byte[] checkEngine(VMPCKSA3Engine engine)
+ {
+ byte[] output = new byte[input.length];
+ engine.processBytes(input, 0, output.length, output, 0);
+
+ checkByte(output, 0, (byte) 0xB6);
+ checkByte(output, 1, (byte) 0xEB);
+ checkByte(output, 2, (byte) 0xAE);
+ checkByte(output, 3, (byte) 0xFE);
+ checkByte(output, 252, (byte) 0x48);
+ checkByte(output, 253, (byte) 0x17);
+ checkByte(output, 254, (byte) 0x24);
+ checkByte(output, 255, (byte) 0x73);
+ checkByte(output, 1020, (byte) 0x1D);
+ checkByte(output, 1021, (byte) 0xAE);
+ checkByte(output, 1022, (byte) 0xC3);
+ checkByte(output, 1023, (byte) 0x5A);
+ checkByte(output, 102396, (byte) 0x1D);
+ checkByte(output, 102397, (byte) 0xA7);
+ checkByte(output, 102398, (byte) 0xE1);
+ checkByte(output, 102399, (byte) 0xDC);
+
+ return output;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new VMPCKSA3Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCMacTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCMacTest.java
new file mode 100644
index 00000000..50a0410b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCMacTest.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.macs.VMPCMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class VMPCMacTest extends SimpleTest
+{
+ public String getName()
+ {
+ return "VMPC-MAC";
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new VMPCMacTest());
+ }
+
+ static byte[] output1 = Hex.decode("9BDA16E2AD0E284774A3ACBC8835A8326C11FAAD");
+
+ public void performTest() throws Exception
+ {
+ CipherParameters kp = new KeyParameter(
+ Hex.decode("9661410AB797D8A9EB767C21172DF6C7"));
+ CipherParameters kpwiv = new ParametersWithIV(kp,
+ Hex.decode("4B5C2F003E67F39557A8D26F3DA2B155"));
+
+ byte[] m = new byte[256];
+ for (int i = 0; i < 256; i++)
+ {
+ m[i] = (byte) i;
+ }
+
+ VMPCMac mac = new VMPCMac();
+ mac.init(kpwiv);
+
+ mac.update(m, 0, m.length);
+
+ byte[] out = new byte[20];
+ mac.doFinal(out, 0);
+
+ if (!Arrays.areEqual(out, output1))
+ {
+ fail("Fail", new String(Hex.encode(output1)), new String(Hex.encode(out)));
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCTest.java
new file mode 100644
index 00000000..cedffa46
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/VMPCTest.java
@@ -0,0 +1,97 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.engines.VMPCEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * VMPC Test
+ */
+public class VMPCTest extends SimpleTest
+{
+ private static final byte[] input = new byte[1000000];
+
+ public String getName()
+ {
+ return "VMPC";
+ }
+
+ private void checkByte(byte[] array, int position, byte b)
+ {
+ if (array[position] != b)
+ {
+ fail("Fail on position " + position,
+ new String(Hex.encode(new byte[] { b })),
+ new String(Hex.encode(new byte[] { array[position] })));
+ }
+ }
+
+ public void performTest()
+ {
+ byte[] key = Hex.decode("9661410AB797D8A9EB767C21172DF6C7");
+ byte[] iv = Hex.decode("4B5C2F003E67F39557A8D26F3DA2B155");
+ CipherParameters kp = new KeyParameter(key);
+ CipherParameters kpwiv = new ParametersWithIV(kp, iv);
+
+ VMPCEngine engine = new VMPCEngine();
+
+ try
+ {
+ engine.init(true, kp);
+ fail("init failed to throw expected exception");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // Expected
+ }
+
+ engine.init(true, kpwiv);
+ checkEngine(engine);
+
+ engine.reset();
+ byte[] output = checkEngine(engine);
+
+ engine.init(false, kpwiv);
+ byte[] recovered = new byte[output.length];
+ engine.processBytes(output, 0, output.length, recovered, 0);
+
+ if (!Arrays.areEqual(input, recovered))
+ {
+ fail("decrypted bytes differ from original bytes");
+ }
+ }
+
+ private byte[] checkEngine(VMPCEngine engine)
+ {
+ byte[] output = new byte[input.length];
+ engine.processBytes(input, 0, output.length, output, 0);
+
+ checkByte(output, 0, (byte) 0xA8);
+ checkByte(output, 1, (byte) 0x24);
+ checkByte(output, 2, (byte) 0x79);
+ checkByte(output, 3, (byte) 0xF5);
+ checkByte(output, 252, (byte) 0xB8);
+ checkByte(output, 253, (byte) 0xFC);
+ checkByte(output, 254, (byte) 0x66);
+ checkByte(output, 255, (byte) 0xA4);
+ checkByte(output, 1020, (byte) 0xE0);
+ checkByte(output, 1021, (byte) 0x56);
+ checkByte(output, 1022, (byte) 0x40);
+ checkByte(output, 1023, (byte) 0xA5);
+ checkByte(output, 102396, (byte) 0x81);
+ checkByte(output, 102397, (byte) 0xCA);
+ checkByte(output, 102398, (byte) 0x49);
+ checkByte(output, 102399, (byte) 0x9A);
+
+ return output;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new VMPCTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/WhirlpoolDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/WhirlpoolDigestTest.java
new file mode 100644
index 00000000..542e6e6b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/WhirlpoolDigestTest.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.WhirlpoolDigest;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * ISO vector test for Whirlpool
+ *
+ */
+public class WhirlpoolDigestTest
+ extends DigestTest
+{
+ private static String[] messages =
+ {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "abcdbcdecdefdefgefghfghighijhijk"
+ };
+
+ private static String[] digests =
+ {
+ "19FA61D75522A4669B44E39C1D2E1726C530232130D407F89AFEE0964997F7A73E83BE698B288FEBCF88E3E03C4F0757EA8964E59B63D93708B138CC42A66EB3",
+ "8ACA2602792AEC6F11A67206531FB7D7F0DFF59413145E6973C45001D0087B42D11BC645413AEFF63A42391A39145A591A92200D560195E53B478584FDAE231A",
+ "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5",
+ "378C84A4126E2DC6E56DCC7458377AAC838D00032230F53CE1F5700C0FFB4D3B8421557659EF55C106B4B52AC5A4AAA692ED920052838F3362E86DBD37A8903E",
+ "F1D754662636FFE92C82EBB9212A484A8D38631EAD4238F5442EE13B8054E41B08BF2A9251C30B6A0B8AAE86177AB4A6F68F673E7207865D5D9819A3DBA4EB3B",
+ "DC37E008CF9EE69BF11F00ED9ABA26901DD7C28CDEC066CC6AF42E40F82F3A1E08EBA26629129D8FB7CB57211B9281A65517CC879D7B962142C65F5A7AF01467",
+ "466EF18BABB0154D25B9D38A6414F5C08784372BCCB204D6549C4AFADB6014294D5BD8DF2A6C44E538CD047B2681A51A2C60481E88C5A20B2C2A80CF3A9A083B",
+ "2A987EA40F917061F5D6F0A0E4644F488A7A5A52DEEE656207C562F988E95C6916BDC8031BC5BE1B7B947639FE050B56939BAAA0ADFF9AE6745B7B181C3BE3FD"
+ };
+
+ WhirlpoolDigestTest()
+ {
+ super(new WhirlpoolDigest(), messages, digests);
+ }
+
+ protected Digest cloneDigest(Digest digest)
+ {
+ return new WhirlpoolDigest((WhirlpoolDigest)digest);
+ }
+
+ private static String _millionAResultVector = "0C99005BEB57EFF50A7CF005560DDF5D29057FD86B20BFD62DECA0F1CCEA4AF51FC15490EDDC47AF32BB2B66C34FF9AD8C6008AD677F77126953B226E4ED8B01";
+
+ private static String _thirtyOneZeros = "3E3F188F8FEBBEB17A933FEAF7FE53A4858D80C915AD6A1418F0318E68D49B4E459223CD414E0FBC8A57578FD755D86E827ABEF4070FC1503E25D99E382F72BA";
+
+ public String getName()
+ {
+ return "Whirlpool";
+ }
+
+ public void performTest()
+ {
+ super.performTest();
+
+ byte[] thirtyOneZeros = new byte[31];
+ performStandardVectorTest("31 zeroes test",
+ thirtyOneZeros, _thirtyOneZeros);
+
+ byte[] millionAInByteArray = new byte[1000000];
+ Arrays.fill(millionAInByteArray, (byte)'a');
+
+ performStandardVectorTest("Million 'a' test",
+ millionAInByteArray, _millionAResultVector);
+ }
+
+ private void performStandardVectorTest(String testTitle, byte[] inputBytes,
+ String resultsAsHex)
+ {
+ doPerformTest(testTitle, inputBytes, resultsAsHex);
+ }
+
+ private void doPerformTest(String testTitle, byte[] inputBytes, String resultsAsHex)
+ {
+ String resStr = createHexOutputFromDigest(inputBytes);
+ if (!resultsAsHex.equals(resStr.toUpperCase()))
+ {
+ fail(testTitle, resultsAsHex, resStr);
+ }
+ }
+
+ private String createHexOutputFromDigest(byte[] digestBytes)
+ {
+ String resStr;
+ Digest digest = new WhirlpoolDigest();
+ byte[] resBuf = new byte[digest.getDigestSize()];
+ digest.update(digestBytes, 0, digestBytes.length);
+ digest.doFinal(resBuf, 0);
+ resStr = new String(Hex.encode(resBuf));
+ return resStr;
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new WhirlpoolDigestTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/X931SignerTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/X931SignerTest.java
new file mode 100644
index 00000000..a77e760f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/X931SignerTest.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.crypto.test;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.digests.SHA224Digest;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.crypto.signers.X931Signer;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class X931SignerTest
+ extends SimpleTest
+{
+ public String getName()
+ {
+ return "X931Signer";
+ }
+
+ public void performTest() throws Exception
+ {
+ BigInteger rsaPubMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+ BigInteger rsaPubExp = new BigInteger(Base64.decode("EQ=="));
+ BigInteger rsaPrivMod = new BigInteger(Base64.decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+ BigInteger rsaPrivDP = new BigInteger(Base64.decode("JXzfzG5v+HtLJIZqYMUefJfFLu8DPuJGaLD6lI3cZ0babWZ/oPGoJa5iHpX4Ul/7l3s1PFsuy1GhzCdOdlfRcQ=="));
+ BigInteger rsaPrivDQ = new BigInteger(Base64.decode("YNdJhw3cn0gBoVmMIFRZzflPDNthBiWy/dUMSRfJCxoZjSnr1gysZHK01HteV1YYNGcwPdr3j4FbOfri5c6DUQ=="));
+ BigInteger rsaPrivExp = new BigInteger(Base64.decode("DxFAOhDajr00rBjqX+7nyZ/9sHWRCCp9WEN5wCsFiWVRPtdB+NeLcou7mWXwf1Y+8xNgmmh//fPV45G2dsyBeZbXeJwB7bzx9NMEAfedchyOwjR8PYdjK3NpTLKtZlEJ6Jkh4QihrXpZMO4fKZWUm9bid3+lmiq43FwW+Hof8/E="));
+ BigInteger rsaPrivP = new BigInteger(Base64.decode("AJ9StyTVW+AL/1s7RBtFwZGFBgd3zctBqzzwKPda6LbtIFDznmwDCqAlIQH9X14X7UPLokCDhuAa76OnDXb1OiE="));
+ BigInteger rsaPrivQ = new BigInteger(Base64.decode("AM3JfD79dNJ5A3beScSzPtWxx/tSLi0QHFtkuhtSizeXdkv5FSba7lVzwEOGKHmW829bRoNxThDy4ds1IihW1w0="));
+ BigInteger rsaPrivQinv = new BigInteger(Base64.decode("Lt0g7wrsNsQxuDdB8q/rH8fSFeBXMGLtCIqfOec1j7FEIuYA/ACiRDgXkHa0WgN7nLXSjHoy630wC5Toq8vvUg=="));
+ RSAKeyParameters rsaPublic = new RSAKeyParameters(false, rsaPubMod, rsaPubExp);
+ RSAPrivateCrtKeyParameters rsaPrivate = new RSAPrivateCrtKeyParameters(rsaPrivMod, rsaPubExp, rsaPrivExp, rsaPrivP, rsaPrivQ, rsaPrivDP, rsaPrivDQ, rsaPrivQinv);
+
+ byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+ X931Signer signer = new X931Signer(new RSAEngine(), new SHA1Digest());
+ signer.init(true, rsaPrivate);
+ signer.update(msg, 0, msg.length);
+ byte[] sig = signer.generateSignature();
+
+ signer = new X931Signer(new RSAEngine(), new SHA1Digest());
+ signer.init(false, rsaPublic);
+ signer.update(msg, 0, msg.length);
+ if (!signer.verifySignature(sig))
+ {
+ fail("X9.31 Signer failed.");
+ }
+
+ shouldPassSignatureTest1();
+ shouldPassSignatureTest2();
+ shouldPassSignatureTest3();
+ }
+
+ private void shouldPassSignatureTest1()
+ throws Exception
+ {
+ BigInteger n = new BigInteger("c9be1b28f8caccca65d86cc3c9bbcc13eccc059df3b80bd2292b811eff3aa0dd75e1e85c333b8e3fa9bed53bb20f5359ff4e6900c5e9a388e3a4772a583a79e2299c76582c2b27694b65e9ba22e66bfb817f8b70b22206d7d8ae488c86dbb7137c26d5eff9b33c90e6cee640630313b7a715802e15142fef498c404a8de19674974785f0f852e2d470fe85a2e54ffca9f5851f672b71df691785a5cdabe8f14aa628942147de7593b2cf962414a5b59c632c4e14f1768c0ab2e9250824beea60a3529f11bf5e070ce90a47686eb0be1086fb21f0827f55295b4a48307db0b048c05a4aec3f488c576ca6f1879d354224c7e84cbcd8e76dd217a3de54dba73c35", 16);
+ BigInteger e = new BigInteger("e75b1b", 16);
+ byte[] msg = Hex.decode("5bb0d1c0ef9b5c7af2477fe08d45523d3842a4b2db943f7033126c2a7829bacb3d2cfc6497ec91688189e81b7f8742488224ba320ce983ce9480722f2cc5bc42611f00bb6311884f660ccc244788378673532edb05284fd92e83f6f6dab406209032e6af9a33c998677933e32d6fb95fd27408940d7728f9c9c40267ca1d20ce");
+ byte[] sig = Hex.decode("0fe8bb8e3109a1eb7489ef35bf4c1a0780071da789c8bd226a4170538eafefdd30b732d628f0e87a0b9450051feae9754d4fb61f57862d10f0bacc4f660d13281d0cd1141c006ade5186ff7d961a4c6cd0a4b352fc1295c5afd088f80ac1f8e192ef116a010a442655fe8ff5eeacea15807906fb0f0dfa86e680d4c005872357f7ece9aa4e20b15d5f709b30f08648ecaa34f2fbf54eb6b414fa2ff6f87561f70163235e69ccb4ac82a2e46d3be214cc2ef5263b569b2d8fd839b21a9e102665105ea762bda25bb446cfd831487a6b846100dee113ae95ae64f4af22c428c87bab809541c962bb3a56d4c86588e0af4ebc7fcc66dadced311051356d3ea745f7");
+
+ RSAKeyParameters rsaPublic = new RSAKeyParameters(false, n, e);
+ X931Signer signer = new X931Signer(new RSAEngine(), new SHA1Digest());
+
+ signer.init(false, rsaPublic);
+
+ signer.update(msg, 0, msg.length);
+
+ if (!signer.verifySignature(sig))
+ {
+ fail("RSA X931 verify test 1 failed.");
+ }
+ }
+
+ private void shouldPassSignatureTest2()
+ throws Exception
+ {
+ BigInteger n = new BigInteger("b746ba6c3c0be64bbe33aa55b2929b0af4e86d773d44bfe5914db9287788c4663984b61a418d2eecca30d752ff6b620a07ec72eeb2b422d2429da352407b99982800b9dd7697be6a7b1baa98ca5f4fc2fe33400f20b9dba337ac25c987804165d4a6e0ee4d18eabd6de5abdfe578cae6713ff91d16c80a5bb20217fe614d9509e75a43e1825327b9da8f0a9f6eeaa1c04b69fb4bacc073569fff4ab491becbe6d0441d437fc3fa823239c4a0f75321666b68dd3f66e2dd394089a15bcc288a68a4eb0a48e17d639743b9dea0a91cc35820544732aff253f8ca9967c609dc01c2f8cd0313a7a91cfa94ff74289a1d2b6f19d1811f4b9a65f4cce9e5759b4cc64f", 16);
+ BigInteger e = new BigInteger("dcbbdb", 16);
+ byte[] msg = Hex.decode("a5d3c8a060f897bbbc20ae0955052f37fbc70986b6e11c65075c9f457142bfa93856897c69020aa81a91b5e4f39e05cdeecc63395ab849c8262ca8bc5c96870aecb8edb0aba0024a9bdb71e06de6100344e5c318bc979ef32b8a49a8278ba99d4861bce42ebbc5c8c666aaa6cac39aff8779f2cae367620f9edd4cb1d80b6c8c");
+ byte[] sig = Hex.decode("39fbbd1804c689a533b0043f84da0f06081038c0fbf31e443e46a05e58f50de5198bbca40522afefaba3aed7082a6cb93b1da39f1f5a42246bf64930781948d300549bef0f8d554ecfca60a1b1ecba95a7014ee4545ad4f0c4e3a31942c6738b4ccd6244b6a21267dadf0826a5f713f13b1f5a9ab8501d957a26d4948278ac67851071a315674bdab173bfef2c2690c8373da6bf3d69f30c0e5da8883de872f59521b40793854085641adf98d13db991c5d0a8aaa0222934fa33332e90ef0b954e195cb267d6ffb36c96e14d1ec7b915a87598b4461a3146566354dc2ae748c84ee0cd46543b53ebff8cdf47725b280a1f799fb6ebb4a31ad2bdd5178250f83a");
+
+ RSAKeyParameters rsaPublic = new RSAKeyParameters(false, n, e);
+ X931Signer signer = new X931Signer(new RSAEngine(), new SHA224Digest());
+
+ signer.init(false, rsaPublic);
+
+ signer.update(msg, 0, msg.length);
+
+ if (!signer.verifySignature(sig))
+ {
+ fail("RSA X931 verify test 2 failed.");
+ }
+ }
+
+ private void shouldPassSignatureTest3()
+ throws Exception
+ {
+ BigInteger n = new BigInteger("dcb5686a3d2063a3f9cf7b9b32d2d3765b4c449b09b4960245a9111cd3b0cbd3260496885b8e1fa5db33b03efcc759d9c1afe29d93c6faebc7e0efada334b5b9a29655e2da2c8f11103d8203be311feab7ae88e9f1b2ec7d8fc655d77202b1681dd9717ec0f525b35584987e19539635a1ed23ca482a00149c609a23dc1645fd", 16);
+ BigInteger e = new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc9f7", 16);
+ BigInteger d = new BigInteger("189d6345099098992e0c9ca5f281e1338092342fa0acc85cc2a111f30f9bd2fb4753cd1a48ef0ddca9bf1af33ec76fb2e23a9fb4896c26f2235b516f7c05ef7ae81e70f4b491a5fedba9b935e9c76d761a813ce7776ff8a1e5efe1166ff2eca26aa900da88c908d51af9de26977fe39719cc781df32216fa41b838f0c63803c3", 16);
+
+ byte[] msg = Hex.decode("911475c6e210ef4ac65b6fe8d2bfe5e01b959771b137c4ef69b88716e0d2ff9ebc1fad0f358c1dd7d50cc99a7b893ac9a6207076f08d8467d9e48c69c683bfe64a44dabaa3f7c243880f6ab7229bf7bb587822314fc5de5131983bfb2eef8b4bc1eac36f353724b567cd1ae8cddd64ddb7057549d5c81ad5fa3b5e751f00abf5");
+ byte[] sig = Hex.decode("02c50ec0ac8a7f38ef5630c396964d6a6daaa7e3083ab5b57fa2a2632f3b70e2e85c8456cd774d45d7e44fcb063f0f04fff9f1e3adfda11272535a92cb59320b190b5ee4261f23d6ceaa925df3a7bfa42e26bf61ea9645d9d64b3c90a820802768a6e209c9f83705375a3867afccc037e8242a98fa4c3db6b2d9877754d47289");
+
+ RSAKeyParameters rsaPublic = new RSAKeyParameters(false, n, e);
+ X931Signer signer = new X931Signer(new RSAEngine(), new SHA1Digest());
+
+ signer.init(true, new RSAKeyParameters(true, n, d));
+
+ signer.update(msg, 0, msg.length);
+
+ byte[] s = signer.generateSignature();
+
+ if (!Arrays.areEqual(sig, s))
+ {
+ fail("RSA X931 sig test 3 failed.");
+ }
+
+ signer.init(false, rsaPublic);
+
+ signer.update(msg, 0, msg.length);
+
+ if (!signer.verifySignature(sig))
+ {
+ fail("RSA X931 verify test 3 failed.");
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new X931SignerTest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/XSalsa20Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/XSalsa20Test.java
new file mode 100644
index 00000000..efbd5ec0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/XSalsa20Test.java
@@ -0,0 +1,166 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.XSalsa20Engine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class XSalsa20Test extends SimpleTest
+{
+ private static class TestCase
+ {
+
+ private byte[] key;
+ private byte[] iv;
+ private byte[] plaintext;
+ private byte[] ciphertext;
+
+ public TestCase(String key, String iv, String plaintext, String ciphertext)
+ {
+ this.key = Hex.decode(key);
+ this.iv = Hex.decode(iv);
+ this.plaintext = Hex.decode(plaintext);
+ this.ciphertext = Hex.decode(ciphertext);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public byte[] getIv()
+ {
+ return iv;
+ }
+
+ public byte[] getPlaintext()
+ {
+ return plaintext;
+ }
+
+ public byte[] getCiphertext()
+ {
+ return ciphertext;
+ }
+ }
+
+ // Test cases generated by naclcrypto-20090308, as used by cryptopp
+ private static final TestCase[] TEST_CASES = new TestCase[] {
+ new TestCase(
+ "a6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff88030",
+ "9e645a74e9e0a60d8243acd9177ab51a1beb8d5a2f5d700c",
+ "093c5e5585579625337bd3ab619d615760d8c5b224a85b1d0efe0eb8a7ee163abb0376529fcc09bab506c618e13ce777d82c3ae9d1a6f972d4160287cbfe60bf2130fc0a6ff6049d0a5c8a82f429231f008082e845d7e189d37f9ed2b464e6b919e6523a8c1210bd52a02a4c3fe406d3085f5068d1909eeeca6369abc981a42e87fe665583f0ab85ae71f6f84f528e6b397af86f6917d9754b7320dbdc2fea81496f2732f532ac78c4e9c6cfb18f8e9bdf74622eb126141416776971a84f94d156beaf67aecbf2ad412e76e66e8fad7633f5b6d7f3d64b5c6c69ce29003c6024465ae3b89be78e915d88b4b5621d",
+ "b2af688e7d8fc4b508c05cc39dd583d6714322c64d7f3e63147aede2d9534934b04ff6f337b031815cd094bdbc6d7a92077dce709412286822ef0737ee47f6b7ffa22f9d53f11dd2b0a3bb9fc01d9a88f9d53c26e9365c2c3c063bc4840bfc812e4b80463e69d179530b25c158f543191cff993106511aa036043bbc75866ab7e34afc57e2cce4934a5faae6eabe4f221770183dd060467827c27a354159a081275a291f69d946d6fe28ed0b9ce08206cf484925a51b9498dbde178ddd3ae91a8581b91682d860f840782f6eea49dbb9bd721501d2c67122dea3b7283848c5f13e0c0de876bd227a856e4de593a3"),
+ new TestCase(
+ "9e1da239d155f52ad37f75c7368a536668b051952923ad44f57e75ab588e475a",
+ "af06f17859dffa799891c4288f6635b5c5a45eee9017fd72",
+ "feac9d54fc8c115ae247d9a7e919dd76cfcbc72d32cae4944860817cbdfb8c04e6b1df76a16517cd33ccf1acda9206389e9e318f5966c093cfb3ec2d9ee2de856437ed581f552f26ac2907609df8c613b9e33d44bfc21ff79153e9ef81a9d66cc317857f752cc175fd8891fefebb7d041e6517c3162d197e2112837d3bc4104312ad35b75ea686e7c70d4ec04746b52ff09c421451459fb59f",
+ "2c261a2f4e61a62e1b27689916bf03453fcbc97bb2af6f329391ef063b5a219bf984d07d70f602d85f6db61474e9d9f5a2deecb4fcd90184d16f3b5b5e168ee03ea8c93f3933a22bc3d1a5ae8c2d8b02757c87c073409052a2a8a41e7f487e041f9a49a0997b540e18621cad3a24f0a56d9b19227929057ab3ba950f6274b121f193e32e06e5388781a1cb57317c0ba6305e910961d01002f0"),
+ new TestCase("d5c7f6797b7e7e9c1d7fd2610b2abf2bc5a7885fb3ff78092fb3abe8986d35e2",
+ "744e17312b27969d826444640e9c4a378ae334f185369c95",
+ "7758298c628eb3a4b6963c5445ef66971222be5d1a4ad839715d1188071739b77cc6e05d5410f963a64167629757",
+ "27b8cfe81416a76301fd1eec6a4d99675069b2da2776c360db1bdfea7c0aa613913e10f7a60fec04d11e65f2d64e"),
+ new TestCase(
+ "737d7811ce96472efed12258b78122f11deaec8759ccbd71eac6bbefa627785c",
+ "6fb2ee3dda6dbd12f1274f126701ec75c35c86607adb3edd",
+ "501325fb2645264864df11faa17bbd58312b77cad3d94ac8fb8542f0eb653ad73d7fce932bb874cb89ac39fc47f8267cf0f0c209f204b2d8578a3bdf461cb6a271a468bebaccd9685014ccbc9a73618c6a5e778a21cc8416c60ad24ddc417a130d53eda6dfbfe47d09170a7be1a708b7b5f3ad464310be36d9a2a95dc39e83d38667e842eb6411e8a23712297b165f690c2d7ca1b1346e3c1fccf5cafd4f8be0",
+ "6724c372d2e9074da5e27a6c54b2d703dc1d4c9b1f8d90f00c122e692ace7700eadca942544507f1375b6581d5a8fb39981c1c0e6e1ff2140b082e9ec016fce141d5199647d43b0b68bfd0fea5e00f468962c7384dd6129aea6a3fdfe75abb210ed5607cef8fa0e152833d5ac37d52e557b91098a322e76a45bbbcf4899e790618aa3f4c2e5e0fc3de93269a577d77a5502e8ea02f717b1dd2df1ec69d8b61ca"),
+ new TestCase(
+ "760158da09f89bbab2c99e6997f9523a95fcef10239bcca2573b7105f6898d34",
+ "43636b2cc346fc8b7c85a19bf507bdc3dafe953b88c69dba",
+ "d30a6d42dff49f0ed039a306bae9dec8d9e88366cc19e8c3642fd58fa0794ebf8029d949730339b0823a51f0f49f0d2c71f1051c1e0e2c86941f172789cdb1b0107413e70f982ff9761877bb526ef1c3eb1106a948d60ef21bd35d32cfd64f89b79ed63ecc5cca56246af736766f285d8e6b0da9cb1cd21020223ffacc5a32",
+ "c815b6b79b64f9369aec8dce8c753df8a50f2bc97c70ce2f014db33a65ac5816bac9e30ac08bdded308c65cb87e28e2e71b677dc25c5a6499c1553555daf1f55270a56959dffa0c66f24e0af00951ec4bb59ccc3a6c5f52e0981647e53e439313a52c40fa7004c855b6e6eb25b212a138e843a9ba46edb2a039ee82a263abe"),
+ new TestCase(
+ "27ba7e81e7edd4e71be53c07ce8e633138f287e155c7fa9e84c4ad804b7fa1b9",
+ "ea05f4ebcd2fb6b000da0612861ba54ff5c176fb601391aa",
+ "e09ff5d2cb050d69b2d42494bde5825238c756d6991d99d7a20d1ef0b83c371c89872690b2fc11d5369f4fc4971b6d3d6c078aef9b0f05c0e61ab89c025168054defeb03fef633858700c58b1262ce011300012673e893e44901dc18eee3105699c44c805897bdaf776af1833162a21a",
+ "a23e7ef93c5d0667c96d9e404dcbe6be62026fa98f7a3ff9ba5d458643a16a1cef7272dc6097a9b52f35983557c77a11b314b4f7d5dc2cca15ee47616f861873cbfed1d32372171a61e38e447f3cf362b3abbb2ed4170d89dcb28187b7bfd206a3e026f084a7e0ed63d319de6bc9afc0"),
+ new TestCase("6799d76e5ffb5b4920bc2768bafd3f8c16554e65efcf9a16f4683a7a06927c11",
+ "61ab951921e54ff06d9b77f313a4e49df7a057d5fd627989", "472766", "8fd7df"),
+ new TestCase(
+ "f68238c08365bb293d26980a606488d09c2f109edafa0bbae9937b5cc219a49c",
+ "5190b51e9b708624820b5abdf4e40fad1fb950ad1adc2d26",
+ "47ec6b1f73c4b7ff5274a0bfd7f45f864812c85a12fbcb3c2cf8a3e90cf66ccf2eacb521e748363c77f52eb426ae57a0c6c78f75af71284569e79d1a92f949a9d69c4efc0b69902f1e36d7562765543e2d3942d9f6ff5948d8a312cff72c1afd9ea3088aff7640bfd265f7a9946e606abc77bcedae6bddc75a0dba0bd917d73e3bd1268f727e0096345da1ed25cf553ea7a98fea6b6f285732de37431561ee1b3064887fbcbd71935e02",
+ "36160e88d3500529ba4edba17bc24d8cfaca9a0680b3b1fc97cf03f3675b7ac301c883a68c071bc54acdd3b63af4a2d72f985e51f9d60a4c7fd481af10b2fc75e252fdee7ea6b6453190617dcc6e2fe1cd56585fc2f0b0e97c5c3f8ad7eb4f31bc4890c03882aac24cc53acc1982296526690a220271c2f6e326750d3fbda5d5b63512c831f67830f59ac49aae330b3e0e02c9ea0091d19841f1b0e13d69c9fbfe8a12d6f30bb734d9d2"),
+ new TestCase(
+ "45b2bd0de4ed9293ec3e26c4840faaf64b7d619d51e9d7a2c7e36c83d584c3df",
+ "546c8c5d6be8f90952cab3f36d7c1957baaa7a59abe3d7e5",
+ "5007c8cd5b3c40e17d7fe423a87ae0ced86bec1c39dc07a25772f3e96dabd56cd3fd7319f6c9654925f2d87087a700e1b130da796895d1c9b9acd62b266144067d373ed51e787498b03c52faad16bb3826fa511b0ed2a19a8663f5ba2d6ea7c38e7212e9697d91486c49d8a000b9a1935d6a7ff7ef23e720a45855481440463b4ac8c4f6e7062adc1f1e1e25d3d65a31812f58a71160",
+ "8eacfba568898b10c0957a7d44100685e8763a71a69a8d16bc7b3f88085bb9a2f09642e4d09a9f0ad09d0aad66b22610c8bd02ff6679bb92c2c026a216bf425c6be35fb8dae7ff0c72b0efd6a18037c70eed0ca90062a49a3c97fdc90a8f9c2ea536bfdc41918a7582c9927fae47efaa3dc87967b7887dee1bf071734c7665901d9105dae2fdf66b4918e51d8f4a48c60d19fbfbbcba"),
+ new TestCase(
+ "fe559c9a282beb40814d016d6bfcb2c0c0d8bf077b1110b8703a3ce39d70e0e1",
+ "b076200cc7011259805e18b304092754002723ebec5d6200",
+ "6db65b9ec8b114a944137c821fd606be75478d928366d5284096cdef782fcff7e8f59cb8ffcda979757902c5ffa6bc477ceaa4cb5d5ea76f94d91e833f823a6bc78f1055dfa6a97bea8965c1cde67a668e001257334a585727d9e0f7c1a06e88d3d25a4e6d9096c968bf138e116a3ebeffd4bb4808adb1fd698164ba0a35c709a47f16f1f4435a2345a9194a00b95abd51851d505809a6077da9baca5831afff31578c487ee68f2767974a98a7e803aac788da98319c4ea8eaa3d394855651f484cef543f537e35158ee29",
+ "4dce9c8f97a028051b0727f34e1b9ef21f06f0760f36e71713204027902090ba2bb6b13436ee778d9f50530efbd7a32b0d41443f58ccaee781c7b716d3a96fdec0e3764ed7959f34c3941278591ea033b5cbadc0f1916032e9bebbd1a8395b83fb63b1454bd775bd20b3a2a96f951246ac14daf68166ba62f6cbff8bd121ac9498ff8852fd2be975df52b5daef3829d18eda42e715022dcbf930d0a789ee6a146c2c7088c35773c63c06b4af4559856ac199ced86863e4294707825337c5857970eb7fddeb263781309011"),
+ new TestCase(
+ "0ae10012d7e56614b03dcc89b14bae9242ffe630f3d7e35ce8bbb97bbc2c92c3",
+ "f96b025d6cf46a8a12ac2af1e2aef1fb83590adadaa5c5ea",
+ "ea0f354e96f12bc72bbaa3d12b4a8ed879b042f0689878f46b651cc4116d6f78409b11430b3aaa30b2076891e8e1fa528f2fd169ed93dc9f84e24409eec2101daf4d057be2492d11de640cbd7b355ad29fb70400fffd7cd6d425abeeb732a0eaa4330af4c656252c4173deab653eb85c58462d7ab0f35fd12b613d29d473d330310dc323d3c66348bbdbb68a326324657cae7b77a9e34358f2cec50c85609e73056856796e3be8d62b6e2fe9f953",
+ "e8abd48924b54e5b80866be7d4ebe5cf4274cafff08b39cb2d40a8f0b472398aedc776e0793812fbf1f60078635d2ed86b15efcdba60411ee23b07233592a44ec31b1013ce8964236675f8f183aef885e864f2a72edf4215b5338fa2b54653dfa1a8c55ce5d95cc605b9b311527f2e3463ffbec78a9d1d65dabad2f338769c9f43f133a791a11c7eca9af0b771a4ac32963dc8f631a2c11217ac6e1b9430c1aae1ceebe22703f429998a8fb8c641"),
+ new TestCase(
+ "082c539bc5b20f97d767cd3f229eda80b2adc4fe49c86329b5cd6250a9877450",
+ "845543502e8b64912d8f2c8d9fffb3c69365686587c08d0c",
+ "a96bb7e910281a6dfad7c8a9c370674f0ceec1ad8d4f0de32f9ae4a23ed329e3d6bc708f876640a229153ac0e7281a8188dd77695138f01cda5f41d5215fd5c6bdd46d982cb73b1efe2997970a9fdbdb1e768d7e5db712068d8ba1af6067b5753495e23e6e1963af012f9c7ce450bf2de619d3d59542fb55f3",
+ "835da74fc6de08cbda277a7966a07c8dcd627e7b17adde6d930b6581e3124b8baad096f693991fedb1572930601fc7709541839b8e3ffd5f033d2060d999c6c6e3048276613e648000acb5212cc632a916afce290e20ebdf612d08a6aa4c79a74b070d3f872a861f8dc6bb07614db515d363349d3a8e3336a3"),
+ new TestCase("3d02bff3375d403027356b94f514203737ee9a85d2052db3e4e5a217c259d18a",
+ "74216c95031895f48c1dba651555ebfa3ca326a755237025",
+ "0d4b0f54fd09ae39baa5fa4baccf2e6682e61b257e01f42b8f",
+ "16c4006c28365190411eb1593814cf15e74c22238f210afc3d"),
+ new TestCase(
+ "ad1a5c47688874e6663a0f3fa16fa7efb7ecadc175c468e5432914bdb480ffc6",
+ "e489eed440f1aae1fac8fb7a9825635454f8f8f1f52e2fcc",
+ "aa6c1e53580f03a9abb73bfdadedfecada4c6b0ebe020ef10db745e54ba861caf65f0e40dfc520203bb54d29e0a8f78f16b3f1aa525d6bfa33c54726e59988cfbec78056",
+ "02fe84ce81e178e7aabdd3ba925a766c3c24756eefae33942af75e8b464556b5997e616f3f2dfc7fce91848afd79912d9fb55201b5813a5a074d2c0d4292c1fd441807c5"),
+ new TestCase(
+ "053a02bedd6368c1fb8afc7a1b199f7f7ea2220c9a4b642a6850091c9d20ab9c",
+ "c713eea5c26dad75ad3f52451e003a9cb0d649f917c89dde",
+ "8f0a8a164760426567e388840276de3f95cb5e3fadc6ed3f3e4fe8bc169d9388804dcb94b6587dbb66cb0bd5f87b8e98b52af37ba290629b858e0e2aa7378047a26602",
+ "516710e59843e6fbd4f25d0d8ca0ec0d47d39d125e9dad987e0518d49107014cb0ae405e30c2eb3794750bca142ce95e290cf95abe15e822823e2e7d3ab21bc8fbd445"),
+ new TestCase(
+ "5b14ab0fbed4c58952548a6cb1e0000cf4481421f41288ea0aa84add9f7deb96",
+ "54bf52b911231b952ba1a6af8e45b1c5a29d97e2abad7c83",
+ "37fb44a675978b560ff9a4a87011d6f3ad2d37a2c3815b45a3c0e6d1b1d8b1784cd468927c2ee39e1dccd4765e1c3d676a335be1ccd6900a45f5d41a317648315d8a8c24adc64eb285f6aeba05b9029586353d303f17a807658b9ff790474e1737bd5fdc604aeff8dfcaf1427dcc3aacbb0256badcd183ed75a2dc52452f87d3c1ed2aa583472b0ab91cda20614e9b6fdbda3b49b098c95823cc72d8e5b717f2314b0324e9ce",
+ "ae6deb5d6ce43d4b09d0e6b1c0e9f46157bcd8ab50eaa3197ff9fa2bf7af649eb52c68544fd3adfe6b1eb316f1f23538d470c30dbfec7e57b60cbcd096c782e7736b669199c8253e70214cf2a098fda8eac5da79a9496a3aae754d03b17c6d70d1027f42bf7f95ce3d1d9c338854e158fcc803e4d6262fb639521e47116ef78a7a437ca9427ba645cd646832feab822a208278e45e93e118d780b988d65397eddfd7a819526e"),
+ new TestCase(
+ "d74636e3413a88d85f322ca80fb0bd650bd0bf0134e2329160b69609cd58a4b0",
+ "efb606aa1d9d9f0f465eaa7f8165f1ac09f5cb46fecf2a57",
+ "f85471b75f6ec81abac2799ec09e98e280b2ffd64ca285e5a0109cfb31ffab2d617b2c2952a2a8a788fc0da2af7f530758f74f1ab56391ab5ff2adbcc5be2d6c7f49fbe8118104c6ff9a23c6dfe52f57954e6a69dcee5db06f514f4a0a572a9a8525d961dae72269b987189d465df6107119c7fa790853e063cba0fab7800ca932e258880fd74c33c784675bedad0e7c09e9cc4d63dd5e9713d5d4a0196e6b562226ac31b4f57c04f90a181973737ddc7e80f364112a9fbb435ebdbcabf7d490ce52",
+ "b2b795fe6c1d4c83c1327e015a67d4465fd8e32813575cbab263e20ef05864d2dc17e0e4eb81436adfe9f638dcc1c8d78f6b0306baf938e5d2ab0b3e05e735cc6fff2d6e02e3d60484bea7c7a8e13e23197fea7b04d47d48f4a4e5944174539492800d3ef51e2ee5e4c8a0bdf050c2dd3dd74fce5e7e5c37364f7547a11480a3063b9a0a157b15b10a5a954de2731ced055aa2e2767f0891d4329c426f3808ee867bed0dc75b5922b7cfb895700fda016105a4c7b7f0bb90f029f6bbcb04ac36ac16") };
+
+ public String getName()
+ {
+ return "XSalsa20";
+ }
+
+ public void performTest() throws Exception
+ {
+ for (int i = 0; i < TEST_CASES.length; i++)
+ {
+ performTest(i, TEST_CASES[i]);
+ }
+ }
+
+ private void performTest(int number, TestCase testCase)
+ {
+ final byte[] plaintext = testCase.getPlaintext();
+ byte[] output = new byte[plaintext.length];
+
+ XSalsa20Engine engine = new XSalsa20Engine();
+ engine.init(false, new ParametersWithIV(new KeyParameter(testCase.getKey()), testCase.getIv()));
+
+ engine.processBytes(testCase.getPlaintext(), 0, testCase.getPlaintext().length, output, 0);
+
+ if (!Arrays.areEqual(testCase.getCiphertext(), output))
+ {
+ fail("mismatch on " + number, new String(Hex.encode(testCase.getCiphertext())),
+ new String(Hex.encode(output)));
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ runTest(new XSalsa20Test());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/XTEATest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/XTEATest.java
new file mode 100644
index 00000000..2b5279e9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/XTEATest.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.XTEAEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * TEA tester - based on C implementation results from http://www.simonshepherd.supanet.com/tea.htm
+ */
+public class XTEATest
+ extends CipherTest
+{
+ static SimpleTest[] tests = {
+ new BlockCipherVectorTest(0, new XTEAEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "0000000000000000",
+ "dee9d4d8f7131ed9"),
+ new BlockCipherVectorTest(1, new XTEAEngine(),
+ new KeyParameter(Hex.decode("00000000000000000000000000000000")),
+ "0102030405060708",
+ "065c1b8975c6a816"),
+ new BlockCipherVectorTest(2, new XTEAEngine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0000000000000000",
+ "1ff9a0261ac64264"),
+ new BlockCipherVectorTest(3, new XTEAEngine(),
+ new KeyParameter(Hex.decode("0123456712345678234567893456789A")),
+ "0102030405060708",
+ "8c67155b2ef91ead"),
+ };
+
+ XTEATest()
+ {
+ super(tests, new XTEAEngine(), new KeyParameter(new byte[16]));
+ }
+
+ public String getName()
+ {
+ return "XTEA";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new XTEATest());
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/test/package.html
new file mode 100644
index 00000000..7bb5e6b1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Example code and test classes for the lightweight API.
+</body>
+</html>
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 7d4fd033..a433b95e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
@@ -29,6 +29,34 @@ public abstract class AbstractTlsClient
this.cipherFactory = cipherFactory;
}
+ protected boolean allowUnexpectedServerExtension(Integer extensionType, byte[] extensionData)
+ throws IOException
+ {
+ switch (extensionType.intValue())
+ {
+ case ExtensionType.elliptic_curves:
+ /*
+ * Exception added based on field reports that some servers do send this, although the
+ * Supported Elliptic Curves Extension is clearly intended to be client-only. If
+ * present, we still require that it is a valid EllipticCurveList.
+ */
+ TlsECCUtils.readSupportedEllipticCurvesExtension(extensionData);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected void checkForUnexpectedServerExtension(Hashtable serverExtensions, Integer extensionType)
+ throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(serverExtensions, extensionType);
+ if (extensionData != null && !allowUnexpectedServerExtension(extensionType, extensionData))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
public void init(TlsClientContext context)
{
this.context = context;
@@ -63,6 +91,16 @@ public abstract class AbstractTlsClient
return ProtocolVersion.TLSv12;
}
+ public boolean isFallback()
+ {
+ /*
+ * draft-ietf-tls-downgrade-scsv-00 4. [..] is meant for use by clients that repeat a
+ * connection attempt with a downgraded protocol in order to avoid interoperability problems
+ * with legacy servers.
+ */
+ return false;
+ }
+
public Hashtable getClientExtensions()
throws IOException
{
@@ -78,27 +116,7 @@ public abstract class AbstractTlsClient
{
// 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 };
-
- // TODO Sort out ECDSA signatures and add them as the preferred option here
- short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa };
-
- this.supportedSignatureAlgorithms = new Vector();
- for (int i = 0; i < hashAlgorithms.length; ++i)
- {
- for (int j = 0; j < signatureAlgorithms.length; ++j)
- {
- this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(hashAlgorithms[i],
- signatureAlgorithms[j]));
- }
- }
-
- /*
- * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1.
- */
- this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
- SignatureAlgorithm.dsa));
+ this.supportedSignatureAlgorithms = TlsUtils.getDefaultSupportedSignatureAlgorithms();
clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions);
@@ -176,21 +194,17 @@ public abstract class AbstractTlsClient
/*
* RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension.
*/
- if (serverExtensions.containsKey(TlsUtils.EXT_signature_algorithms))
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
+ checkForUnexpectedServerExtension(serverExtensions, TlsUtils.EXT_signature_algorithms);
+
+ checkForUnexpectedServerExtension(serverExtensions, TlsECCUtils.EXT_elliptic_curves);
- int[] namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(serverExtensions);
- if (namedCurves != null)
+ if (TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions);
}
-
- this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions);
- if (this.serverECPointFormats != null && !TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
+ else
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ checkForUnexpectedServerExtension(serverExtensions, TlsECCUtils.EXT_ec_point_formats);
}
}
}
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 5e02892e..42057255 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
@@ -2,9 +2,22 @@ package org.bouncycastle.crypto.tls;
import java.security.SecureRandom;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.prng.DigestRandomGenerator;
+import org.bouncycastle.crypto.prng.RandomGenerator;
+import org.bouncycastle.util.Times;
+
abstract class AbstractTlsContext
implements TlsContext
{
+ private static long counter = Times.nanoTime();
+
+ private synchronized static long nextCounterValue()
+ {
+ return ++counter;
+ }
+
+ private RandomGenerator nonceRandom;
private SecureRandom secureRandom;
private SecurityParameters securityParameters;
@@ -15,10 +28,24 @@ abstract class AbstractTlsContext
AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters)
{
+ Digest d = TlsUtils.createHash(HashAlgorithm.sha256);
+ byte[] seed = new byte[d.getDigestSize()];
+ secureRandom.nextBytes(seed);
+
+ this.nonceRandom = new DigestRandomGenerator(d);
+ nonceRandom.addSeedMaterial(nextCounterValue());
+ nonceRandom.addSeedMaterial(Times.nanoTime());
+ nonceRandom.addSeedMaterial(seed);
+
this.secureRandom = secureRandom;
this.securityParameters = securityParameters;
}
+ public RandomGenerator getNonceRandomGenerator()
+ {
+ return nonceRandom;
+ }
+
public SecureRandom getSecureRandom()
{
return secureRandom;
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 80d6af73..a4c9c057 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
@@ -5,6 +5,17 @@ import java.io.IOException;
public abstract class AbstractTlsPeer
implements TlsPeer
{
+ public boolean shouldUseGMTUnixTime()
+ {
+ /*
+ * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
+ * TLS implementors MUST by default set the entire value the ClientHello.Random and
+ * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
+ * sequence.
+ */
+ return false;
+ }
+
public void notifySecureRenegotiation(boolean secureRenegotiation) throws IOException
{
if (!secureRenegotiation)
@@ -17,7 +28,7 @@ public abstract class AbstractTlsPeer
}
}
- public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause)
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
{
}
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 bd428a99..9ba0ce66 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
@@ -19,6 +19,7 @@ public abstract class AbstractTlsServer
protected short[] offeredCompressionMethods;
protected Hashtable clientExtensions;
+ protected boolean encryptThenMACOffered;
protected short maxFragmentLengthOffered;
protected boolean truncatedHMacOffered;
protected Vector supportedSignatureAlgorithms;
@@ -41,6 +42,11 @@ public abstract class AbstractTlsServer
this.cipherFactory = cipherFactory;
}
+ protected boolean allowEncryptThenMAC()
+ {
+ return true;
+ }
+
protected boolean allowTruncatedHMac()
{
return false;
@@ -85,7 +91,8 @@ public abstract class AbstractTlsServer
for (int i = 0; i < namedCurves.length; ++i)
{
int namedCurve = namedCurves[i];
- if (!NamedCurve.refersToASpecificNamedCurve(namedCurve) || TlsECCUtils.isSupportedNamedCurve(namedCurve))
+ if (NamedCurve.isValid(namedCurve)
+ && (!NamedCurve.refersToASpecificNamedCurve(namedCurve) || TlsECCUtils.isSupportedNamedCurve(namedCurve)))
{
return true;
}
@@ -105,6 +112,20 @@ public abstract class AbstractTlsServer
this.clientVersion = clientVersion;
}
+ public void notifyFallback(boolean isFallback) throws IOException
+ {
+ /*
+ * draft-ietf-tls-downgrade-scsv-00 3. If TLS_FALLBACK_SCSV appears in
+ * ClientHello.cipher_suites and the highest protocol version supported by the server is
+ * higher than the version indicated in ClientHello.client_version, the server MUST respond
+ * with an inappropriate_fallback alert.
+ */
+ if (isFallback && getMaximumVersion().isLaterVersionOf(clientVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.inappropriate_fallback);
+ }
+ }
+
public void notifyOfferedCipherSuites(int[] offeredCipherSuites)
throws IOException
{
@@ -125,6 +146,7 @@ public abstract class AbstractTlsServer
if (clientExtensions != null)
{
+ this.encryptThenMACOffered = TlsExtensionsUtils.hasEncryptThenMACExtension(clientExtensions);
this.maxFragmentLengthOffered = TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions);
this.truncatedHMacOffered = TlsExtensionsUtils.hasTruncatedHMacExtension(clientExtensions);
@@ -197,9 +219,9 @@ public abstract class AbstractTlsServer
{
int cipherSuite = cipherSuites[i];
- // TODO Certain cipher suites may only be available starting at a particular version
if (Arrays.contains(this.offeredCipherSuites, cipherSuite)
- && (eccCipherSuitesEnabled || !TlsECCUtils.isECCCipherSuite(cipherSuite)))
+ && (eccCipherSuitesEnabled || !TlsECCUtils.isECCCipherSuite(cipherSuite))
+ && TlsUtils.isValidCipherSuiteForVersion(cipherSuite, serverVersion))
{
return this.selectedCipherSuite = cipherSuite;
}
@@ -225,7 +247,21 @@ public abstract class AbstractTlsServer
public Hashtable getServerExtensions()
throws IOException
{
- if (this.maxFragmentLengthOffered >= 0)
+ if (this.encryptThenMACOffered && allowEncryptThenMAC())
+ {
+ /*
+ * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
+ * and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
+ * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
+ * client.
+ */
+ if (TlsUtils.isBlockCipherSuite(this.selectedCipherSuite))
+ {
+ TlsExtensionsUtils.addEncryptThenMACExtension(checkServerExtensions());
+ }
+ }
+
+ if (this.maxFragmentLengthOffered >= 0 && MaxFragmentLength.isValid(maxFragmentLengthOffered))
{
TlsExtensionsUtils.addMaxFragmentLengthExtension(checkServerExtensions(), this.maxFragmentLengthOffered);
}
@@ -242,8 +278,8 @@ 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.uncompressed,
+ ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
TlsECCUtils.addSupportedPointFormatsExtension(checkServerExtensions(), serverECPointFormats);
}
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 91366be0..3db267e2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
@@ -213,4 +213,91 @@ public class AlertDescription
* "unknown_psk_identity" alert message.
*/
public static final short unknown_psk_identity = 115;
+
+ /*
+ * draft-ietf-tls-downgrade-scsv-00
+ */
+
+ /**
+ * If TLS_FALLBACK_SCSV appears in ClientHello.cipher_suites and the highest protocol version
+ * supported by the server is higher than the version indicated in ClientHello.client_version,
+ * the server MUST respond with an inappropriate_fallback alert.
+ */
+ public static final short inappropriate_fallback = 86;
+
+ public static String getName(short alertDescription)
+ {
+ switch (alertDescription)
+ {
+ case close_notify:
+ return "close_notify";
+ case unexpected_message:
+ return "unexpected_message";
+ case bad_record_mac:
+ return "bad_record_mac";
+ case decryption_failed:
+ return "decryption_failed";
+ case record_overflow:
+ return "record_overflow";
+ case decompression_failure:
+ return "decompression_failure";
+ case handshake_failure:
+ return "handshake_failure";
+ case no_certificate:
+ return "no_certificate";
+ case bad_certificate:
+ return "bad_certificate";
+ case unsupported_certificate:
+ return "unsupported_certificate";
+ case certificate_revoked:
+ return "certificate_revoked";
+ case certificate_expired:
+ return "certificate_expired";
+ case certificate_unknown:
+ return "certificate_unknown";
+ case illegal_parameter:
+ return "illegal_parameter";
+ case unknown_ca:
+ return "unknown_ca";
+ case access_denied:
+ return "access_denied";
+ case decode_error:
+ return "decode_error";
+ case decrypt_error:
+ return "decrypt_error";
+ case export_restriction:
+ return "export_restriction";
+ case protocol_version:
+ return "protocol_version";
+ case insufficient_security:
+ return "insufficient_security";
+ case internal_error:
+ return "internal_error";
+ case user_canceled:
+ return "user_canceled";
+ case no_renegotiation:
+ return "no_renegotiation";
+ case unsupported_extension:
+ return "unsupported_extension";
+ case certificate_unobtainable:
+ return "certificate_unobtainable";
+ case unrecognized_name:
+ return "unrecognized_name";
+ case bad_certificate_status_response:
+ return "bad_certificate_status_response";
+ case bad_certificate_hash_value:
+ return "bad_certificate_hash_value";
+ case unknown_psk_identity:
+ return "unknown_psk_identity";
+ case inappropriate_fallback:
+ return "inappropriate_fallback";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static String getText(short alertDescription)
+ {
+ return getName(alertDescription) + "(" + alertDescription + ")";
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java
index b0b131d6..cd468305 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertLevel.java
@@ -1,10 +1,28 @@
package org.bouncycastle.crypto.tls;
/**
- * RFC 2246 7.2
+ * RFC 5246 7.2
*/
public class AlertLevel
{
public static final short warning = 1;
public static final short fatal = 2;
+
+ public static String getName(short alertDescription)
+ {
+ switch (alertDescription)
+ {
+ case warning:
+ return "warning";
+ case fatal:
+ return "fatal";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static String getText(short alertDescription)
+ {
+ return getName(alertDescription) + "(" + alertDescription + ")";
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlwaysValidVerifyer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlwaysValidVerifyer.java
deleted file mode 100644
index bf4cd13b..00000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlwaysValidVerifyer.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.bouncycastle.crypto.tls;
-
-/**
- * A certificate verifyer, that will always return true.
- * <p/>
- * <pre>
- * DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING.
- * </pre>
- *
- * @deprecated Perform certificate verification in TlsAuthentication implementation
- */
-public class AlwaysValidVerifyer
- implements CertificateVerifyer
-{
- /**
- * Return true.
- *
- * @see org.bouncycastle.crypto.tls.CertificateVerifyer#isValid(org.bouncycastle.asn1.x509.Certificate[])
- */
- public boolean isValid(org.bouncycastle.asn1.x509.Certificate[] certs)
- {
- return true;
- }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/BasicTlsPSKIdentity.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/BasicTlsPSKIdentity.java
new file mode 100644
index 00000000..fa65d54c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/BasicTlsPSKIdentity.java
@@ -0,0 +1,42 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+public class BasicTlsPSKIdentity
+ implements TlsPSKIdentity
+{
+ protected byte[] identity;
+ protected byte[] psk;
+
+ public BasicTlsPSKIdentity(byte[] identity, byte[] psk)
+ {
+ this.identity = Arrays.clone(identity);
+ this.psk = Arrays.clone(psk);
+ }
+
+ public BasicTlsPSKIdentity(String identity, byte[] psk)
+ {
+ this.identity = Strings.toUTF8ByteArray(identity);
+ this.psk = Arrays.clone(psk);
+ }
+
+ public void skipIdentityHint()
+ {
+ }
+
+ public void notifyIdentityHint(byte[] psk_identity_hint)
+ {
+ }
+
+ public byte[] getPSKIdentity()
+ {
+ return identity;
+ }
+
+ public byte[] getPSK()
+ {
+ return psk;
+ }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java
index 595bdad2..7f013b3a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/BulkCipherAlgorithm.java
@@ -2,13 +2,12 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 2246
- * <p/>
+ * <p>
* Note that the values here are implementation-specific and arbitrary. It is recommended not to
* depend on the particular values (e.g. serialization).
*/
public class BulkCipherAlgorithm
{
-
public static final int _null = 0;
public static final int rc4 = 1;
public static final int rc2 = 2;
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 7642c4a7..cfa72df8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
@@ -8,7 +8,7 @@ public class ByteQueue
/**
* @return The smallest number which can be written as 2^x which is bigger than i.
*/
- public static final int nextTwoPow(int i)
+ public static int nextTwoPow(int i)
{
/*
* This code is based of a lot of code I found on the Internet which mostly
@@ -30,7 +30,7 @@ public class ByteQueue
/**
* The buffer where we store our data.
*/
- private byte[] databuf;;
+ private byte[] databuf;
/**
* How many bytes at the beginning of the buffer are skipped.
@@ -62,15 +62,15 @@ public class ByteQueue
*/
public void read(byte[] buf, int offset, int len, int skip)
{
- if ((available - skip) < len)
- {
- throw new TlsRuntimeException("Not enough data to read");
- }
if ((buf.length - offset) < len)
{
- throw new TlsRuntimeException("Buffer size of " + buf.length
+ throw new IllegalArgumentException("Buffer size of " + buf.length
+ " is too small for a read of " + len + " bytes");
}
+ if ((available - skip) < len)
+ {
+ throw new IllegalStateException("Not enough data to read");
+ }
System.arraycopy(databuf, skipped + skip, buf, offset, len);
}
@@ -112,7 +112,7 @@ public class ByteQueue
{
if (i > available)
{
- throw new TlsRuntimeException("Cannot remove " + i + " bytes, only got " + available);
+ throw new IllegalStateException("Cannot remove " + i + " bytes, only got " + available);
}
/*
@@ -142,11 +142,19 @@ public class ByteQueue
removeData(buf, 0, len, skip);
return buf;
}
+
+ /**
+ * @deprecated Use 'available' instead
+ */
+ public int size()
+ {
+ return available;
+ }
/**
* @return The number of bytes which are available in this buffer.
*/
- public int size()
+ public int available()
{
return available;
}
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 02cf6931..38f7d5bf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
@@ -11,12 +11,11 @@ import org.bouncycastle.asn1.ASN1Primitive;
/**
* Parsing and encoding of a <i>Certificate</i> struct from RFC 4346.
- * <p/>
* <pre>
- * opaque ASN.1Cert<2^24-1>;
+ * opaque ASN.1Cert&lt;2^24-1&gt;;
*
* struct {
- * ASN.1Cert certificate_list<0..2^24-1>;
+ * ASN.1Cert certificate_list&lt;0..2^24-1&gt;;
* } Certificate;
* </pre>
*
@@ -40,14 +39,6 @@ public class Certificate
}
/**
- * @deprecated use {@link #getCertificateList()} instead
- */
- public org.bouncycastle.asn1.x509.Certificate[] getCerts()
- {
- return getCertificateList();
- }
-
- /**
* @return an array of {@link org.bouncycastle.asn1.x509.Certificate} representing a certificate
* chain.
*/
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 e9606e3f..68e051ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
@@ -12,11 +12,10 @@ import org.bouncycastle.asn1.x500.X500Name;
/**
* Parsing and encoding of a <i>CertificateRequest</i> struct from RFC 4346.
- * <p/>
* <pre>
* struct {
- * ClientCertificateType certificate_types<1..2^8-1>;
- * DistinguishedName certificate_authorities<3..2^16-1>;
+ * ClientCertificateType certificate_types&lt;1..2^8-1&gt;;
+ * DistinguishedName certificate_authorities&lt;3..2^16-1&gt;;
* } CertificateRequest;
* </pre>
*
@@ -29,11 +28,6 @@ public class CertificateRequest
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
- * verify, listed in descending order of preference.
- */
-
/**
* @param certificateTypes see {@link ClientCertificateType} for valid constants.
* @param certificateAuthorities a {@link Vector} of {@link X500Name}.
@@ -47,7 +41,7 @@ public class CertificateRequest
/**
* @return an array of certificate types
- * @see {@link ClientCertificateType}
+ * @see ClientCertificateType
*/
public short[] getCertificateTypes()
{
@@ -108,7 +102,7 @@ public class CertificateRequest
X500Name certificateAuthority = (X500Name)certificateAuthorities.elementAt(i);
byte[] derEncoding = certificateAuthority.getEncoded(ASN1Encoding.DER);
derEncodings.addElement(derEncoding);
- totalLength += derEncoding.length;
+ totalLength += derEncoding.length + 2;
}
TlsUtils.checkUint16(totalLength);
@@ -116,8 +110,8 @@ public class CertificateRequest
for (int i = 0; i < derEncodings.size(); ++i)
{
- byte[] encDN = (byte[])derEncodings.elementAt(i);
- output.write(encDN);
+ byte[] derEncoding = (byte[])derEncodings.elementAt(i);
+ TlsUtils.writeOpaque16(derEncoding, output);
}
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateVerifyer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateVerifyer.java
deleted file mode 100644
index 2e3715c1..00000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateVerifyer.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.bouncycastle.crypto.tls;
-
-/**
- * This should be implemented by any class which can find out, if a given certificate
- * chain is being accepted by an client.
- *
- * @deprecated Perform certificate verification in TlsAuthentication implementation
- */
-public interface CertificateVerifyer
-{
- /**
- * @param certs The certs, which are part of the chain.
- * @return True, if the chain is accepted, false otherwise.
- */
- public boolean isValid(org.bouncycastle.asn1.x509.Certificate[] certs);
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Chacha20Poly1305.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Chacha20Poly1305.java
new file mode 100644
index 00000000..a82045bf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Chacha20Poly1305.java
@@ -0,0 +1,156 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.engines.ChaChaEngine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
+import org.bouncycastle.crypto.macs.Poly1305;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Pack;
+
+public class Chacha20Poly1305 implements TlsCipher
+{
+ protected TlsContext context;
+
+ protected ChaChaEngine encryptCipher;
+ protected ChaChaEngine decryptCipher;
+
+ public Chacha20Poly1305(TlsContext context) throws IOException
+ {
+ if (!TlsUtils.isTLSv12(context))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.context = context;
+
+ byte[] key_block = TlsUtils.calculateKeyBlock(context, 64);
+
+ KeyParameter client_write_key = new KeyParameter(key_block, 0, 32);
+ KeyParameter server_write_key = new KeyParameter(key_block, 32, 32);
+
+ this.encryptCipher = new ChaChaEngine(20);
+ this.decryptCipher = new ChaChaEngine(20);
+
+ KeyParameter encryptKey, decryptKey;
+ if (context.isServer())
+ {
+ encryptKey = server_write_key;
+ decryptKey = client_write_key;
+ }
+ else
+ {
+ encryptKey = client_write_key;
+ decryptKey = server_write_key;
+ }
+
+ byte[] dummyNonce = new byte[8];
+
+ this.encryptCipher.init(true, new ParametersWithIV(encryptKey, dummyNonce));
+ this.decryptCipher.init(false, new ParametersWithIV(decryptKey, dummyNonce));
+ }
+
+ public int getPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit - 16;
+ }
+
+ public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len) throws IOException
+ {
+ int ciphertextLength = len + 16;
+
+ KeyParameter macKey = initRecordMAC(encryptCipher, true, seqNo);
+
+ byte[] output = new byte[ciphertextLength];
+ encryptCipher.processBytes(plaintext, offset, len, output, 0);
+
+ byte[] additionalData = getAdditionalData(seqNo, type, len);
+ byte[] mac = calculateRecordMAC(macKey, additionalData, output, 0, len);
+ System.arraycopy(mac, 0, output, len, mac.length);
+
+ return output;
+ }
+
+ 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);
+ }
+
+ int plaintextLength = len - 16;
+
+ byte[] receivedMAC = Arrays.copyOfRange(ciphertext, offset + plaintextLength, offset + len);
+
+ KeyParameter macKey = initRecordMAC(decryptCipher, false, seqNo);
+
+ byte[] additionalData = getAdditionalData(seqNo, type, plaintextLength);
+ byte[] calculatedMAC = calculateRecordMAC(macKey, additionalData, ciphertext, offset, plaintextLength);
+
+ if (!Arrays.constantTimeAreEqual(calculatedMAC, receivedMAC))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ byte[] output = new byte[plaintextLength];
+ decryptCipher.processBytes(ciphertext, offset, plaintextLength, output, 0);
+
+ return output;
+ }
+
+ protected KeyParameter initRecordMAC(ChaChaEngine cipher, boolean forEncryption, long seqNo)
+ {
+ byte[] nonce = new byte[8];
+ TlsUtils.writeUint64(seqNo, nonce, 0);
+
+ cipher.init(forEncryption, new ParametersWithIV(null, nonce));
+
+ byte[] firstBlock = new byte[64];
+ cipher.processBytes(firstBlock, 0, firstBlock.length, firstBlock, 0);
+
+ // NOTE: The BC implementation puts 'r' after 'k'
+ System.arraycopy(firstBlock, 0, firstBlock, 32, 16);
+ KeyParameter macKey = new KeyParameter(firstBlock, 16, 32);
+ Poly1305KeyGenerator.clamp(macKey.getKey());
+ return macKey;
+ }
+
+ protected byte[] calculateRecordMAC(KeyParameter macKey, byte[] additionalData, byte[] buf, int off, int len)
+ {
+ Mac mac = new Poly1305();
+ mac.init(macKey);
+
+ updateRecordMAC(mac, additionalData, 0, additionalData.length);
+ updateRecordMAC(mac, buf, off, len);
+
+ byte[] output = new byte[mac.getMacSize()];
+ mac.doFinal(output, 0);
+ return output;
+ }
+
+ protected void updateRecordMAC(Mac mac, byte[] buf, int off, int len)
+ {
+ mac.update(buf, off, len);
+
+ byte[] longLen = Pack.longToLittleEndian(len & 0xFFFFFFFFL);
+ mac.update(longLen, 0, longLen.length);
+ }
+
+ protected byte[] getAdditionalData(long seqNo, short type, int len) throws IOException
+ {
+ /*
+ * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version +
+ * TLSCompressed.length
+ */
+ byte[] additional_data = new byte[13];
+ TlsUtils.writeUint64(seqNo, additional_data, 0);
+ TlsUtils.writeUint8(type, additional_data, 8);
+ TlsUtils.writeVersion(context.getServerVersion(), additional_data, 9);
+ TlsUtils.writeUint16(len, additional_data, 11);
+
+ return additional_data;
+ }
+}
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 c1e75334..9374cecf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
@@ -56,7 +56,7 @@ public class CipherSuite
public static final int TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A;
/*
- * RFC 4132
+ * RFC 5932
*/
public static final int TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041;
public static final int TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042;
@@ -64,6 +64,7 @@ public class CipherSuite
public static final int TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044;
public static final int TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045;
public static final int TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046;
+
public static final int TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084;
public static final int TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085;
public static final int TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086;
@@ -71,6 +72,20 @@ public class CipherSuite
public static final int TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088;
public static final int TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089;
+ public static final int TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA;
+ public static final int TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB;
+ public static final int TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC;
+ public static final int TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD;
+ public static final int TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE;
+ public static final int TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF;
+
+ public static final int TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0;
+ public static final int TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1;
+ public static final int TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2;
+ public static final int TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3;
+ public static final int TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4;
+ public static final int TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5;
+
/*
* RFC 4162
*/
@@ -240,6 +255,54 @@ public class CipherSuite
public static final int TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
/*
+ * RFC 6367
+ */
+ public static final int TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC072;
+ public static final int TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC073;
+ public static final int TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC074;
+ public static final int TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC075;
+ public static final int TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC076;
+ public static final int TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC077;
+ public static final int TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC078;
+ public static final int TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC079;
+
+ public static final int TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07A;
+ public static final int TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07B;
+ public static final int TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07C;
+ public static final int TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07D;
+ public static final int TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07E;
+ public static final int TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07F;
+ public static final int TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC080;
+ public static final int TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC081;
+ public static final int TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC082;
+ public static final int TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC083;
+ public static final int TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 = 0xC084;
+ public static final int TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 = 0xC085;
+ public static final int TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC086;
+ public static final int TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC087;
+ public static final int TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC088;
+ public static final int TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC089;
+ public static final int TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08A;
+ public static final int TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08B;
+ public static final int TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08C;
+ public static final int TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08D;
+
+ public static final int TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08E;
+ public static final int TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08F;
+ public static final int TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC090;
+ public static final int TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC091;
+ public static final int TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC092;
+ public static final int TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC093;
+ public static final int TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC094;
+ public static final int TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC095;
+ public static final int TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC096;
+ public static final int TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC097;
+ public static final int TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC098;
+ public static final int TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC099;
+ public static final int TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC09A;
+ public static final int TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC09B;
+
+ /*
* RFC 6655
*/
public static final int TLS_RSA_WITH_AES_128_CCM = 0xC09C;
@@ -260,38 +323,54 @@ public class CipherSuite
public static final int TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB;
/*
- * TBD[draft-josefsson-salsa20-tls-02]
+ * RFC 7251
+ */
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 0xC0AC;
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_256_CCM = 0xC0AD;
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE;
+ public static final int TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = 0xC0AF;
+
+ /*
+ * draft-agl-tls-chacha20poly1305-04
+ */
+ public static final int TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC13;
+ public static final int TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC14;
+ public static final int TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC15;
+
+ /*
+ * draft-josefsson-salsa20-tls-04
*/
- 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;
+ public static final int TLS_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE410;
+ public static final int TLS_RSA_WITH_SALSA20_SHA1 = 0xE411;
+ public static final int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE412;
+ public static final int TLS_ECDHE_RSA_WITH_SALSA20_SHA1 = 0xE413;
+ public static final int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE414;
+ public static final int TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1 = 0xE415;
+ public static final int TLS_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE416;
+ public static final int TLS_PSK_WITH_SALSA20_SHA1 = 0xE417;
+ public static final int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE418;
+ public static final int TLS_ECDHE_PSK_WITH_SALSA20_SHA1 = 0xE419;
+ public static final int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE41A;
+ public static final int TLS_RSA_PSK_WITH_SALSA20_SHA1 = 0xE41B;
+ public static final int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE41C;
+ public static final int TLS_DHE_PSK_WITH_SALSA20_SHA1 = 0xE41D;
+ public static final int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE41E;
+ public static final int TLS_DHE_RSA_WITH_SALSA20_SHA1 = 0xE41F;
+
+ /*
+ * draft-ietf-tls-downgrade-scsv-00
+ */
+ public static final int TLS_FALLBACK_SCSV = 0x5600;
+
+ public static boolean isSCSV(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case TLS_EMPTY_RENEGOTIATION_INFO_SCSV:
+ case TLS_FALLBACK_SCSV:
+ return true;
+ default:
+ return false;
+ }
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherType.java
index cac7dbe4..c6d845a6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherType.java
@@ -2,13 +2,12 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 2246
- * <p/>
+ * <p>
* Note that the values here are implementation-specific and arbitrary. It is recommended not to
* depend on the particular values (e.g. serialization).
*/
public class CipherType
{
-
public static final int stream = 0;
public static final int block = 1;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java
index a77a8265..77e64607 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientAuthenticationType.java
@@ -2,7 +2,6 @@ package org.bouncycastle.crypto.tls;
public class ClientAuthenticationType
{
-
/*
* RFC 5077 4
*/
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java
index 0a12acae..6bddfc0f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ClientCertificateType.java
@@ -2,7 +2,6 @@ package org.bouncycastle.crypto.tls;
public class ClientCertificateType
{
-
/*
* RFC 4346 7.4.4
*/
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 43b73bff..70c81a03 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
@@ -78,25 +78,25 @@ class CombinedHash
/**
* @see org.bouncycastle.crypto.Digest#update(byte)
*/
- public void update(byte in)
+ public void update(byte input)
{
- md5.update(in);
- sha1.update(in);
+ md5.update(input);
+ sha1.update(input);
}
/**
* @see org.bouncycastle.crypto.Digest#update(byte[], int, int)
*/
- public void update(byte[] in, int inOff, int len)
+ public void update(byte[] input, int inOff, int len)
{
- md5.update(in, inOff, len);
- sha1.update(in, inOff, len);
+ md5.update(input, inOff, len);
+ sha1.update(input, inOff, len);
}
/**
* @see org.bouncycastle.crypto.Digest#doFinal(byte[], int)
*/
- public int doFinal(byte[] out, int outOff)
+ public int doFinal(byte[] output, int outOff)
{
if (context != null && TlsUtils.isSSL(context))
{
@@ -104,8 +104,8 @@ class CombinedHash
ssl3Complete(sha1, SSL3Mac.IPAD, SSL3Mac.OPAD, 40);
}
- int i1 = md5.doFinal(out, outOff);
- int i2 = sha1.doFinal(out, outOff + i1);
+ int i1 = md5.doFinal(output, outOff);
+ int i2 = sha1.doFinal(output, outOff + i1);
return i1 + i2;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java
index 935d378f..5c3a4fbd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CompressionMethod.java
@@ -7,11 +7,6 @@ public class CompressionMethod
{
public static final short _null = 0;
- /**
- * @deprecated use '_null' instead
- */
- public static final short NULL = _null;
-
/*
* RFC 3749 2
*/
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java
index f13def6e..bcbf607e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ConnectionEnd.java
@@ -2,13 +2,12 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 2246
- * <p/>
+ * <p>
* Note that the values here are implementation-specific and arbitrary. It is recommended not to
* depend on the particular values (e.g. serialization).
*/
public class ConnectionEnd
{
-
public static final int server = 0;
public static final int client = 1;
}
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 b3b1abe7..5d9d4590 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
@@ -32,11 +32,14 @@ public class DTLSClientProtocol
SecurityParameters securityParameters = new SecurityParameters();
securityParameters.entity = ConnectionEnd.client;
- securityParameters.clientRandom = TlsProtocol.createRandomBlock(secureRandom);
ClientHandshakeState state = new ClientHandshakeState();
state.client = client;
state.clientContext = new TlsClientContextImpl(secureRandom, securityParameters);
+
+ securityParameters.clientRandom = TlsProtocol.createRandomBlock(client.shouldUseGMTUnixTime(),
+ state.clientContext.getNonceRandomGenerator());
+
client.init(state.clientContext);
DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.clientContext, client, ContentType.handshake);
@@ -69,7 +72,7 @@ public class DTLSClientProtocol
catch (RuntimeException e)
{
recordLayer.fail(AlertDescription.internal_error);
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
@@ -149,6 +152,10 @@ public class DTLSClientProtocol
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
+ Hashtable sessionServerExtensions = state.sessionParameters.readServerExtensions();
+
+ securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(sessionServerExtensions);
+
securityParameters.masterSecret = Arrays.clone(state.sessionParameters.getMasterSecret());
recordLayer.initPendingEpoch(state.client.getCipher());
@@ -308,11 +315,12 @@ public class DTLSClientProtocol
byte[] clientKeyExchangeBody = generateClientKeyExchange(state);
handshake.sendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody);
+ TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish();
+ securityParameters.sessionHash = TlsProtocol.getCurrentPRFHash(state.clientContext, prepareFinishHash, null);
+
TlsProtocol.establishMasterSecret(state.clientContext, state.keyExchange);
recordLayer.initPendingEpoch(state.client.getCipher());
- TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish();
-
if (state.clientCredentials != null && state.clientCredentials instanceof TlsSignerCredentials)
{
TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials;
@@ -320,23 +328,17 @@ public class DTLSClientProtocol
/*
* RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
*/
- SignatureAndHashAlgorithm signatureAndHashAlgorithm;
- byte[] hash;
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtils.getSignatureAndHashAlgorithm(
+ state.clientContext, signerCredentials);
- if (TlsUtils.isTLSv12(state.clientContext))
+ byte[] hash;
+ if (signatureAndHashAlgorithm == null)
{
- signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm();
- if (signatureAndHashAlgorithm == null)
- {
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
-
- hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash());
+ hash = securityParameters.getSessionHash();
}
else
{
- signatureAndHashAlgorithm = null;
- hash = TlsProtocol.getCurrentPRFHash(state.clientContext, prepareFinishHash, null);
+ hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash());
}
byte[] signature = signerCredentials.generateCertificateSignature(hash);
@@ -377,6 +379,8 @@ public class DTLSClientProtocol
.setCompressionAlgorithm(securityParameters.compressionAlgorithm)
.setMasterSecret(securityParameters.masterSecret)
.setPeerCertificate(serverCertificate)
+ .setPSKIdentity(securityParameters.pskIdentity)
+ .setSRPIdentity(securityParameters.srpIdentity)
.build();
state.tlsSession = TlsUtils.importSession(state.tlsSession.getSessionID(), state.sessionParameters);
@@ -408,10 +412,13 @@ public class DTLSClientProtocol
throw new TlsFatalAlert(AlertDescription.internal_error);
}
- state.clientContext.setClientVersion(client_version);
+ TlsClientContextImpl context = state.clientContext;
+
+ context.setClientVersion(client_version);
TlsUtils.writeVersion(client_version, buf);
- buf.write(state.clientContext.getSecurityParameters().getClientRandom());
+ SecurityParameters securityParameters = context.getSecurityParameters();
+ buf.write(securityParameters.getClientRandom());
// Session ID
byte[] session_id = TlsUtils.EMPTY_BYTES;
@@ -428,6 +435,8 @@ public class DTLSClientProtocol
// Cookie
TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+ boolean fallback = client.isFallback();
+
/*
* Cipher suites
*/
@@ -436,6 +445,8 @@ public class DTLSClientProtocol
// Integer -> byte[]
state.clientExtensions = client.getClientExtensions();
+ securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(state.clientExtensions);
+
// Cipher Suites (and SCSV)
{
/*
@@ -446,14 +457,25 @@ public class DTLSClientProtocol
byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo);
boolean noRenegExt = (null == renegExtData);
- boolean noSCSV = !Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+ boolean noRenegSCSV = !Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
- if (noRenegExt && noSCSV)
+ if (noRenegExt && noRenegSCSV)
{
// TODO Consider whether to default to a client extension instead
state.offeredCipherSuites = Arrays.append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
}
+ /*
+ * draft-ietf-tls-downgrade-scsv-00 4. If a client sends a ClientHello.client_version
+ * containing a lower value than the latest (highest-valued) version supported by the
+ * client, it SHOULD include the TLS_FALLBACK_SCSV cipher suite value in
+ * ClientHello.cipher_suites.
+ */
+ if (fallback && !Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
+ {
+ state.offeredCipherSuites = Arrays.append(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
+ }
+
TlsUtils.writeUint16ArrayWithUint16Length(state.offeredCipherSuites, buf);
}
@@ -618,7 +640,8 @@ public class DTLSClientProtocol
state.selectedCipherSuite = TlsUtils.readUint16(buf);
if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite)
|| state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
- || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ || CipherSuite.isSCSV(state.selectedCipherSuite)
+ || !TlsUtils.isValidCipherSuiteForVersion(state.selectedCipherSuite, server_version))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
@@ -653,6 +676,17 @@ public class DTLSClientProtocol
Hashtable serverExtensions = TlsProtocol.readExtensions(buf);
/*
+ * draft-ietf-tls-session-hash-01 5.2. If a server receives the "extended_master_secret"
+ * extension, it MUST include the "extended_master_secret" extension in its ServerHello
+ * message.
+ */
+ boolean serverSentExtendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(serverExtensions);
+ if (serverSentExtendedMasterSecret != securityParameters.extendedMasterSecret)
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ /*
* RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
* extended client hello message. However, see RFC 5746 exception below. We always include
* the SCSV, so an Extended Server Hello is always allowed.
@@ -665,26 +699,53 @@ public class DTLSClientProtocol
Integer extType = (Integer)e.nextElement();
/*
- * RFC 5746 Note that sending a "renegotiation_info" extension in response to a
+ * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a
* ClientHello containing only the SCSV is an explicit exception to the prohibition
* in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is
* only allowed because the client is signaling its willingness to receive the
- * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. TLS implementations
- * MUST continue to comply with Section 7.4.1.4 for all other extensions.
+ * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
*/
- if (!extType.equals(TlsProtocol.EXT_RenegotiationInfo)
- && null == TlsUtils.getExtensionData(state.clientExtensions, extType))
+ if (extType.equals(TlsProtocol.EXT_RenegotiationInfo))
+ {
+ continue;
+ }
+
+ /*
+ * 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(state.clientExtensions, extType))
{
- /*
- * RFC 3546 2.3 Note that for all extension types (including those defined in
- * future), the extension type MUST NOT appear in the extended server hello
- * unless the same extension type appeared in the corresponding client hello.
- * Thus clients MUST abort the handshake if they receive an extension type in
- * the extended server hello that they did not request in the associated
- * (extended) client hello.
- */
throw new TlsFatalAlert(AlertDescription.unsupported_extension);
}
+
+ /*
+ * draft-ietf-tls-session-hash-01 5.2. Implementation note: if the server decides to
+ * proceed with resumption, the extension does not have any effect. Requiring the
+ * extension to be included anyway makes the extension negotiation logic easier,
+ * because it does not depend on whether resumption is accepted or not.
+ */
+ if (extType.equals(TlsExtensionsUtils.EXT_extended_master_secret))
+ {
+ 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[.]
+ */
+ // TODO[sessions]
+// 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);
+// }
}
/*
@@ -714,6 +775,20 @@ public class DTLSClientProtocol
}
}
+ /*
+ * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
+ * and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
+ * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
+ * client.
+ */
+ boolean serverSentEncryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(serverExtensions);
+ if (serverSentEncryptThenMAC && !TlsUtils.isBlockCipherSuite(state.selectedCipherSuite))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ securityParameters.encryptThenMAC = serverSentEncryptThenMAC;
+
state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, serverExtensions,
AlertDescription.illegal_parameter);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java
index 59fbc53e..aec96cef 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSEpoch.java
@@ -2,13 +2,12 @@ package org.bouncycastle.crypto.tls;
class DTLSEpoch
{
-
private final DTLSReplayWindow replayWindow = new DTLSReplayWindow();
private final int epoch;
private final TlsCipher cipher;
- private long sequence_number = 0;
+ private long sequenceNumber = 0;
DTLSEpoch(int epoch, TlsCipher cipher)
{
@@ -28,7 +27,7 @@ class DTLSEpoch
long allocateSequenceNumber()
{
// TODO Check for overflow
- return sequence_number++;
+ return sequenceNumber++;
}
TlsCipher getCipher()
@@ -46,8 +45,8 @@ class DTLSEpoch
return replayWindow;
}
- long getSequence_number()
+ long getSequenceNumber()
{
- return sequence_number;
+ return sequenceNumber;
}
}
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 e27580c1..db43011b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
@@ -68,23 +68,11 @@ public abstract class DTLSProtocol
protected static void validateSelectedCipherSuite(int selectedCipherSuite, short alertDescription)
throws IOException
{
- switch (selectedCipherSuite)
+ switch (TlsUtils.getEncryptionAlgorithm(selectedCipherSuite))
{
- case CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5:
- case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
- case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_DH_anon_EXPORT_WITH_RC4_40_MD5:
- case CipherSuite.TLS_DH_anon_WITH_RC4_128_MD5:
- case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA:
- // TODO Alert
- throw new IllegalStateException("RC4 MUST NOT be used with DTLS");
+ case EncryptionAlgorithm.RC4_40:
+ case EncryptionAlgorithm.RC4_128:
+ throw new TlsFatalAlert(alertDescription);
}
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java
index d82bcc97..c052dcd9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReassembler.java
@@ -4,7 +4,6 @@ import java.util.Vector;
class DTLSReassembler
{
-
private final short msg_type;
private final byte[] body;
@@ -17,7 +16,7 @@ class DTLSReassembler
this.missing.addElement(new Range(0, length));
}
- short getType()
+ short getMsgType()
{
return msg_type;
}
@@ -28,9 +27,8 @@ class DTLSReassembler
}
void contributeFragment(short msg_type, int length, byte[] buf, int off, int fragment_offset,
- int fragment_length)
+ int fragment_length)
{
-
int fragment_end = fragment_offset + fragment_length;
if (this.msg_type != msg_type || this.body.length != length || fragment_end > length)
@@ -82,15 +80,11 @@ class DTLSReassembler
}
else
{
- if (copyEnd == range.getEnd())
- {
- range.setEnd(copyStart);
- }
- else
+ if (copyEnd != range.getEnd())
{
missing.insertElementAt(new Range(copyEnd, range.getEnd()), ++i);
- range.setEnd(copyStart);
}
+ range.setEnd(copyStart);
}
}
}
@@ -104,7 +98,6 @@ class DTLSReassembler
private static class Range
{
-
private int start, end;
Range(int start, int end)
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 cba13d51..5abd86ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
@@ -245,10 +245,6 @@ class DTLSRecordLayer
closeTransport();
}
}
- else
- {
- // TODO What exception?
- }
continue;
}
@@ -431,7 +427,7 @@ class DTLSRecordLayer
}
}
- private void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause)
+ private void raiseAlert(short alertLevel, short alertDescription, String message, Throwable cause)
throws IOException
{
peer.notifyAlertRaised(alertLevel, alertDescription, message, cause);
@@ -446,17 +442,17 @@ class DTLSRecordLayer
private int receiveRecord(byte[] buf, int off, int len, int waitMillis)
throws IOException
{
- if (recordQueue.size() > 0)
+ if (recordQueue.available() > 0)
{
int length = 0;
- if (recordQueue.size() >= RECORD_HEADER_LENGTH)
+ if (recordQueue.available() >= RECORD_HEADER_LENGTH)
{
byte[] lengthBytes = new byte[2];
recordQueue.read(lengthBytes, 0, 2, 11);
length = TlsUtils.readUint16(lengthBytes, 0);
}
- int received = Math.min(recordQueue.size(), RECORD_HEADER_LENGTH + length);
+ int received = Math.min(recordQueue.available(), RECORD_HEADER_LENGTH + length);
recordQueue.removeData(buf, off, received, 0);
return received;
}
@@ -515,6 +511,6 @@ class DTLSRecordLayer
private static long getMacSequenceNumber(int epoch, long sequence_number)
{
- return ((long)epoch << 48) | sequence_number;
+ return ((epoch & 0xFFFFFFFFL) << 48) | sequence_number;
}
}
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 84ccfcb9..9218d190 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
@@ -97,7 +97,7 @@ class DTLSReliableHandshake
if (body != null)
{
previousInboundFlight = null;
- return updateHandshakeMessagesDigest(new Message(next_receive_seq++, next.getType(), body));
+ return updateHandshakeMessagesDigest(new Message(next_receive_seq++, next.getMsgType(), body));
}
}
}
@@ -107,7 +107,7 @@ class DTLSReliableHandshake
// TODO Check the conditions under which we should reset this
int readTimeoutMillis = 1000;
- for (; ; )
+ for (;;)
{
int receiveLimit = recordLayer.getReceiveLimit();
if (buf == null || buf.length < receiveLimit)
@@ -160,13 +160,11 @@ class DTLSReliableHandshake
.valueOf(seq));
if (reassembler != null)
{
-
reassembler.contributeFragment(msg_type, length, buf, 12, fragment_offset,
fragment_length);
if (checkAll(previousInboundFlight))
{
-
resendOutboundFlight();
/*
@@ -182,7 +180,6 @@ class DTLSReliableHandshake
}
else
{
-
DTLSReassembler reassembler = (DTLSReassembler)currentInboundFlight.get(Integers.valueOf(seq));
if (reassembler == null)
{
@@ -199,7 +196,7 @@ class DTLSReliableHandshake
{
previousInboundFlight = null;
return updateHandshakeMessagesDigest(new Message(next_receive_seq++,
- reassembler.getType(), body));
+ reassembler.getMsgType(), body));
}
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java
index 0a5325be..06537aa3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReplayWindow.java
@@ -2,12 +2,12 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 4347 4.1.2.5 Anti-replay
- * <p/>
+ * <p>
* Support fast rejection of duplicate records by maintaining a sliding receive window
+ * </p>
*/
class DTLSReplayWindow
{
-
private static final long VALID_SEQ_MASK = 0x0000FFFFFFFFFFFFL;
private static final long WINDOW_SIZE = 64L;
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 fbb3336f..2d367888 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
@@ -46,11 +46,14 @@ public class DTLSServerProtocol
SecurityParameters securityParameters = new SecurityParameters();
securityParameters.entity = ConnectionEnd.server;
- securityParameters.serverRandom = TlsProtocol.createRandomBlock(secureRandom);
ServerHandshakeState state = new ServerHandshakeState();
state.server = server;
state.serverContext = new TlsServerContextImpl(secureRandom, securityParameters);
+
+ securityParameters.serverRandom = TlsProtocol.createRandomBlock(server.shouldUseGMTUnixTime(),
+ state.serverContext.getNonceRandomGenerator());
+
server.init(state.serverContext);
DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.serverContext, server, ContentType.handshake);
@@ -74,7 +77,7 @@ public class DTLSServerProtocol
catch (RuntimeException e)
{
recordLayer.fail(AlertDescription.internal_error);
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
@@ -244,11 +247,12 @@ public class DTLSServerProtocol
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
+ TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish();
+ securityParameters.sessionHash = TlsProtocol.getCurrentPRFHash(state.serverContext, prepareFinishHash, null);
+
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
@@ -340,7 +344,8 @@ public class DTLSServerProtocol
state.selectedCipherSuite = state.server.getSelectedCipherSuite();
if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite)
|| state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
- || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ || CipherSuite.isSCSV(state.selectedCipherSuite)
+ || !TlsUtils.isValidCipherSuiteForVersion(state.selectedCipherSuite, server_version))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
@@ -386,8 +391,16 @@ public class DTLSServerProtocol
}
}
+ if (securityParameters.extendedMasterSecret)
+ {
+ state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.serverExtensions);
+ TlsExtensionsUtils.addExtendedMasterSecretExtension(state.serverExtensions);
+ }
+
if (state.serverExtensions != null)
{
+ securityParameters.encryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(state.serverExtensions);
+
state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, state.serverExtensions,
AlertDescription.internal_error);
@@ -467,27 +480,40 @@ public class DTLSServerProtocol
{
ByteArrayInputStream buf = new ByteArrayInputStream(body);
- DigitallySigned clientCertificateVerify = DigitallySigned.parse(state.serverContext, buf);
+ TlsServerContextImpl context = state.serverContext;
+ DigitallySigned clientCertificateVerify = DigitallySigned.parse(context, buf);
TlsProtocol.assertEmpty(buf);
// Verify the CertificateVerify message contains a correct signature.
+ boolean verified = false;
try
{
- // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned
- byte[] certificateVerifyHash = TlsProtocol.getCurrentPRFHash(state.serverContext, prepareFinishHash, null);
+ byte[] hash;
+ if (TlsUtils.isTLSv12(context))
+ {
+ hash = prepareFinishHash.getFinalHash(clientCertificateVerify.getAlgorithm().getHash());
+ }
+ else
+ {
+ hash = context.getSecurityParameters().getSessionHash();
+ }
org.bouncycastle.asn1.x509.Certificate x509Cert = state.clientCertificate.getCertificateAt(0);
SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType);
- tlsSigner.init(state.serverContext);
- tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
- clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash);
+ tlsSigner.init(context);
+ verified = tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
+ clientCertificateVerify.getSignature(), publicKey, hash);
}
catch (Exception e)
{
+ }
+
+ if (!verified)
+ {
throw new TlsFatalAlert(AlertDescription.decrypt_error);
}
}
@@ -545,11 +571,17 @@ public class DTLSServerProtocol
*/
state.clientExtensions = TlsProtocol.readExtensions(buf);
- state.serverContext.setClientVersion(client_version);
+ TlsServerContextImpl context = state.serverContext;
+ SecurityParameters securityParameters = context.getSecurityParameters();
+
+ securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(state.clientExtensions);
+
+ context.setClientVersion(client_version);
state.server.notifyClientVersion(client_version);
+ state.server.notifyFallback(Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV));
- state.serverContext.getSecurityParameters().clientRandom = client_random;
+ securityParameters.clientRandom = client_random;
state.server.notifyOfferedCipherSuites(state.offeredCipherSuites);
state.server.notifyOfferedCompressionMethods(state.offeredCompressionMethods);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java
index a67d7bd6..ea790aac 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSTransport.java
@@ -5,7 +5,6 @@ import java.io.IOException;
public class DTLSTransport
implements DatagramTransport
{
-
private final DTLSRecordLayer recordLayer;
DTLSTransport(DTLSRecordLayer recordLayer)
@@ -45,7 +44,7 @@ public class DTLSTransport
catch (RuntimeException e)
{
recordLayer.fail(AlertDescription.internal_error);
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
@@ -69,7 +68,7 @@ public class DTLSTransport
catch (RuntimeException e)
{
recordLayer.fail(AlertDescription.internal_error);
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java
index df63b185..14972146 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DatagramTransport.java
@@ -4,7 +4,6 @@ import java.io.IOException;
public interface DatagramTransport
{
-
int getReceiveLimit()
throws IOException;
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 7f70c647..f3209434 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
@@ -4,20 +4,13 @@ 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;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.digests.SHA384Digest;
-import org.bouncycastle.crypto.digests.SHA512Digest;
-import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.AESEngine;
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;
@@ -33,6 +26,9 @@ public class DefaultTlsCipherFactory
{
case EncryptionAlgorithm._3DES_EDE_CBC:
return createDESedeCipher(context, macAlgorithm);
+ case EncryptionAlgorithm.AEAD_CHACHA20_POLY1305:
+ // NOTE: Ignores macAlgorithm
+ return createChaCha20Poly1305(context);
case EncryptionAlgorithm.AES_128_CBC:
return createAESCipher(context, 16, macAlgorithm);
case EncryptionAlgorithm.AES_128_CCM:
@@ -57,8 +53,14 @@ public class DefaultTlsCipherFactory
return createCipher_AES_GCM(context, 32, 16);
case EncryptionAlgorithm.CAMELLIA_128_CBC:
return createCamelliaCipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.CAMELLIA_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return createCipher_Camellia_GCM(context, 16, 16);
case EncryptionAlgorithm.CAMELLIA_256_CBC:
return createCamelliaCipher(context, 32, macAlgorithm);
+ case EncryptionAlgorithm.CAMELLIA_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return createCipher_Camellia_GCM(context, 32, 16);
case EncryptionAlgorithm.ESTREAM_SALSA20:
return createSalsa20Cipher(context, 12, 32, macAlgorithm);
case EncryptionAlgorithm.NULL:
@@ -81,6 +83,19 @@ public class DefaultTlsCipherFactory
createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
}
+ protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
+ throws IOException
+ {
+ return new TlsBlockCipher(context, createCamelliaBlockCipher(),
+ createCamelliaBlockCipher(), createHMACDigest(macAlgorithm),
+ createHMACDigest(macAlgorithm), cipherKeySize);
+ }
+
+ protected TlsCipher createChaCha20Poly1305(TlsContext context) throws IOException
+ {
+ return new Chacha20Poly1305(context);
+ }
+
protected TlsAEADCipher createCipher_AES_CCM(TlsContext context, int cipherKeySize, int macSize)
throws IOException
{
@@ -95,12 +110,11 @@ public class DefaultTlsCipherFactory
createAEADBlockCipher_AES_GCM(), cipherKeySize, macSize);
}
- protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
+ protected TlsAEADCipher createCipher_Camellia_GCM(TlsContext context, int cipherKeySize, int macSize)
throws IOException
{
- return new TlsBlockCipher(context, createCamelliaBlockCipher(),
- createCamelliaBlockCipher(), createHMACDigest(macAlgorithm),
- createHMACDigest(macAlgorithm), cipherKeySize);
+ return new TlsAEADCipher(context, createAEADBlockCipher_Camellia_GCM(),
+ createAEADBlockCipher_Camellia_GCM(), cipherKeySize, macSize);
}
protected TlsBlockCipher createDESedeCipher(TlsContext context, int macAlgorithm)
@@ -121,18 +135,14 @@ public class DefaultTlsCipherFactory
throws IOException
{
return new TlsStreamCipher(context, createRC4StreamCipher(), createRC4StreamCipher(),
- createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
+ createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize, false);
}
protected TlsStreamCipher createSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm)
throws IOException
{
- /*
- * 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);
+ createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize, true);
}
protected TlsBlockCipher createSEEDCipher(TlsContext context, int macAlgorithm)
@@ -142,25 +152,41 @@ public class DefaultTlsCipherFactory
createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 16);
}
+ protected BlockCipher createAESEngine()
+ {
+ return new AESEngine();
+ }
+
+ protected BlockCipher createCamelliaEngine()
+ {
+ return new CamelliaEngine();
+ }
+
protected BlockCipher createAESBlockCipher()
{
- return new CBCBlockCipher(new AESFastEngine());
+ return new CBCBlockCipher(createAESEngine());
}
protected AEADBlockCipher createAEADBlockCipher_AES_CCM()
{
- return new CCMBlockCipher(new AESFastEngine());
+ return new CCMBlockCipher(createAESEngine());
}
protected AEADBlockCipher createAEADBlockCipher_AES_GCM()
{
// TODO Consider allowing custom configuration of multiplier
- return new GCMBlockCipher(new AESFastEngine());
+ return new GCMBlockCipher(createAESEngine());
+ }
+
+ protected AEADBlockCipher createAEADBlockCipher_Camellia_GCM()
+ {
+ // TODO Consider allowing custom configuration of multiplier
+ return new GCMBlockCipher(createCamelliaEngine());
}
protected BlockCipher createCamelliaBlockCipher()
{
- return new CBCBlockCipher(new CamelliaEngine());
+ return new CBCBlockCipher(createCamelliaEngine());
}
protected BlockCipher createDESedeBlockCipher()
@@ -190,29 +216,17 @@ public class DefaultTlsCipherFactory
case MACAlgorithm._null:
return null;
case MACAlgorithm.hmac_md5:
- return new MD5Digest();
+ return TlsUtils.createHash(HashAlgorithm.md5);
case MACAlgorithm.hmac_sha1:
- return new SHA1Digest();
+ return TlsUtils.createHash(HashAlgorithm.sha1);
case MACAlgorithm.hmac_sha256:
- return new SHA256Digest();
+ return TlsUtils.createHash(HashAlgorithm.sha256);
case MACAlgorithm.hmac_sha384:
- return new SHA384Digest();
+ return TlsUtils.createHash(HashAlgorithm.sha384);
case MACAlgorithm.hmac_sha512:
- return new SHA512Digest();
+ return TlsUtils.createHash(HashAlgorithm.sha512);
default:
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 63db45f3..0ed8ea0a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
@@ -17,10 +17,21 @@ public abstract class DefaultTlsClient
public int[] getCipherSuites()
{
- 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 };
+ return new int[]
+ {
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ 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_DHE_DSS_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_DHE_DSS_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()
@@ -36,7 +47,11 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
return createDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
@@ -48,7 +63,11 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
return createDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
@@ -60,7 +79,11 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
@@ -76,11 +99,14 @@ public abstract class DefaultTlsClient
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_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
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);
@@ -91,6 +117,10 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
@@ -102,6 +132,10 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
@@ -109,16 +143,23 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
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_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
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:
@@ -128,12 +169,15 @@ 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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
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:
@@ -148,16 +192,18 @@ public abstract class DefaultTlsClient
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_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
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();
@@ -187,6 +233,11 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MACAlgorithm._null);
+
case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
@@ -210,10 +261,12 @@ public abstract class DefaultTlsClient
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_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_ECDHE_ECDSA_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);
@@ -253,10 +306,12 @@ public abstract class DefaultTlsClient
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_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_ECDHE_ECDSA_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);
@@ -278,6 +333,28 @@ public abstract class DefaultTlsClient
case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_GCM, MACAlgorithm._null);
+
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
@@ -285,18 +362,36 @@ 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_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha384);
+
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);
@@ -326,12 +421,6 @@ public abstract class DefaultTlsClient
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 dcdb4936..d5151bce 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
@@ -2,15 +2,10 @@ package org.bouncycastle.crypto.tls;
import java.io.IOException;
-import org.bouncycastle.crypto.InvalidCipherTextException;
-import org.bouncycastle.crypto.encodings.PKCS1Encoding;
-import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
-public class DefaultTlsEncryptionCredentials
- extends AbstractTlsEncryptionCredentials
+public class DefaultTlsEncryptionCredentials extends AbstractTlsEncryptionCredentials
{
protected TlsContext context;
protected Certificate certificate;
@@ -58,18 +53,6 @@ public class DefaultTlsEncryptionCredentials
public byte[] decryptPreMasterSecret(byte[] encryptedPreMasterSecret)
throws IOException
{
-
- PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine());
- encoding.init(false, new ParametersWithRandom(this.privateKey, context.getSecureRandom()));
-
- try
- {
- return encoding.processBlock(encryptedPreMasterSecret, 0,
- encryptedPreMasterSecret.length);
- }
- catch (InvalidCipherTextException e)
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
+ return TlsRSAUtils.safeDecryptPreMasterSecret(context, (RSAKeyParameters)privateKey, encryptedPreMasterSecret);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSRPGroupVerifier.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSRPGroupVerifier.java
new file mode 100644
index 00000000..8fb9baa9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSRPGroupVerifier.java
@@ -0,0 +1,67 @@
+package org.bouncycastle.crypto.tls;
+
+import java.math.BigInteger;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.agreement.srp.SRP6StandardGroups;
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
+
+public class DefaultTlsSRPGroupVerifier
+ implements TlsSRPGroupVerifier
+{
+ protected static final Vector DEFAULT_GROUPS = new Vector();
+
+ static
+ {
+ DEFAULT_GROUPS.addElement(SRP6StandardGroups.rfc5054_1024);
+ DEFAULT_GROUPS.addElement(SRP6StandardGroups.rfc5054_1536);
+ DEFAULT_GROUPS.addElement(SRP6StandardGroups.rfc5054_2048);
+ DEFAULT_GROUPS.addElement(SRP6StandardGroups.rfc5054_3072);
+ DEFAULT_GROUPS.addElement(SRP6StandardGroups.rfc5054_4096);
+ DEFAULT_GROUPS.addElement(SRP6StandardGroups.rfc5054_6144);
+ DEFAULT_GROUPS.addElement(SRP6StandardGroups.rfc5054_8192);
+ }
+
+ // Vector is (SRP6GroupParameters)
+ protected Vector groups;
+
+ /**
+ * Accept only the group parameters specified in RFC 5054 Appendix A.
+ */
+ public DefaultTlsSRPGroupVerifier()
+ {
+ this(DEFAULT_GROUPS);
+ }
+
+ /**
+ * Specify a custom set of acceptable group parameters.
+ *
+ * @param groups a {@link Vector} of acceptable {@link SRP6GroupParameters}
+ */
+ public DefaultTlsSRPGroupVerifier(Vector groups)
+ {
+ this.groups = groups;
+ }
+
+ public boolean accept(SRP6GroupParameters group)
+ {
+ for (int i = 0; i < groups.size(); ++i)
+ {
+ if (areGroupsEqual(group, (SRP6GroupParameters)groups.elementAt(i)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean areGroupsEqual(SRP6GroupParameters a, SRP6GroupParameters b)
+ {
+ return a == b || (areParametersEqual(a.getN(), b.getN()) && areParametersEqual(a.getG(), b.getG()));
+ }
+
+ protected boolean areParametersEqual(BigInteger a, BigInteger b)
+ {
+ return a == b || a.equals(b);
+ }
+}
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 31b6a742..8c483313 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
@@ -8,7 +8,6 @@ import org.bouncycastle.crypto.params.DHParameters;
public abstract class DefaultTlsServer
extends AbstractTlsServer
{
-
public DefaultTlsServer()
{
super();
@@ -19,6 +18,18 @@ public abstract class DefaultTlsServer
super(cipherFactory);
}
+ protected TlsSignerCredentials getDSASignerCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected TlsSignerCredentials getECDSASignerCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
protected TlsEncryptionCredentials getRSAEncryptionCredentials()
throws IOException
{
@@ -38,11 +49,21 @@ public abstract class DefaultTlsServer
protected 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,};
+ return new int[]
+ {
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
+ };
}
public TlsCredentials getCredentials()
@@ -50,6 +71,76 @@ public abstract class DefaultTlsServer
{
switch (selectedCipherSuite)
{
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_DES_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ 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_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return getDSASignerCredentials();
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ 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_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ 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:
+ return getECDSASignerCredentials();
+
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:
@@ -62,7 +153,11 @@ public abstract class DefaultTlsServer
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_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_RSA_WITH_NULL_MD5:
case CipherSuite.TLS_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
@@ -83,7 +178,12 @@ public abstract class DefaultTlsServer
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_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
@@ -92,8 +192,15 @@ 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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
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:
return getRSASignerCredentials();
default:
@@ -117,7 +224,11 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
return createDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
@@ -129,7 +240,11 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
return createDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
@@ -141,7 +256,11 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
@@ -157,11 +276,14 @@ public abstract class DefaultTlsServer
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_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
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);
@@ -172,6 +294,10 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
@@ -183,6 +309,10 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
return createECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
@@ -190,16 +320,23 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
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_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
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:
@@ -209,12 +346,15 @@ 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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
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:
@@ -229,16 +369,18 @@ public abstract class DefaultTlsServer
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_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
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();
@@ -266,6 +408,11 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MACAlgorithm._null);
+
case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
@@ -289,10 +436,12 @@ public abstract class DefaultTlsServer
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_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_ECDHE_ECDSA_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);
@@ -332,10 +481,12 @@ public abstract class DefaultTlsServer
return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_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_ECDHE_ECDSA_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);
@@ -357,6 +508,28 @@ public abstract class DefaultTlsServer
case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha1);
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_GCM, MACAlgorithm._null);
+
case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
@@ -364,18 +537,36 @@ 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_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha384);
+
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);
@@ -405,12 +596,6 @@ public abstract class DefaultTlsServer
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 15bf4a40..3b3391e1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
@@ -93,7 +93,7 @@ public class DefaultTlsSignerCredentials
}
catch (CryptoException e)
{
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestAlgorithm.java
deleted file mode 100644
index 41d54009..00000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestAlgorithm.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.bouncycastle.crypto.tls;
-
-/**
- * RFC 2246
- * <p/>
- * Note that the values here are implementation-specific and arbitrary. It is recommended not to
- * depend on the particular values (e.g. serialization).
- *
- * @deprecated use MACAlgorithm constants instead
- */
-public class DigestAlgorithm
-{
- public static final int NULL = 0;
- public static final int MD5 = 1;
- public static final int SHA = 2;
-
- /*
- * RFC 5246
- */
- public static final int SHA256 = 3;
- public static final int SHA384 = 4;
- public static final int SHA512 = 5;
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java
index 57f0ad01..630dfcd6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ECBasisType.java
@@ -5,7 +5,11 @@ package org.bouncycastle.crypto.tls;
*/
public class ECBasisType
{
-
public static final short ec_basis_trinomial = 1;
public static final short ec_basis_pentanomial = 2;
+
+ public static boolean isValid(short ecBasisType)
+ {
+ return ecBasisType >= ec_basis_trinomial && ecBasisType <= ec_basis_pentanomial;
+ }
}
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 6f3fe010..2da2f76a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
@@ -2,13 +2,12 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 2246
- * <p/>
+ * <p>
* Note that the values here are implementation-specific and arbitrary. It is recommended not to
* depend on the particular values (e.g. serialization).
*/
public class EncryptionAlgorithm
{
-
public static final int NULL = 0;
public static final int RC4_40 = 1;
public static final int RC4_128 = 2;
@@ -31,7 +30,7 @@ public class EncryptionAlgorithm
public static final int AES_256_GCM = 11;
/*
- * RFC 4132
+ * RFC 5932
*/
public static final int CAMELLIA_128_CBC = 12;
public static final int CAMELLIA_256_CBC = 13;
@@ -50,8 +49,19 @@ public class EncryptionAlgorithm
public static final int AES_256_CCM_8 = 18;
/*
- * TBD[draft-josefsson-salsa20-tls-02]
+ * RFC 6367
+ */
+ public static final int CAMELLIA_128_GCM = 19;
+ public static final int CAMELLIA_256_GCM = 20;
+
+ /*
+ * draft-josefsson-salsa20-tls-04
+ */
+ public static final int ESTREAM_SALSA20 = 100;
+ public static final int SALSA20 = 101;
+
+ /*
+ * draft-agl-tls-chacha20poly1305-04
*/
- static final int ESTREAM_SALSA20 = 100;
- static final int SALSA20 = 101;
+ public static final int AEAD_CHACHA20_POLY1305 = 102;
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java
index 902720ac..579d4dc3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExporterLabel.java
@@ -28,4 +28,9 @@ public class ExporterLabel
* RFC 5764
*/
public static final String dtls_srtp = "EXTRACTOR-dtls_srtp";
+
+ /*
+ * draft-ietf-tls-session-hash-01
+ */
+ public static final String extended_master_secret = "extended master secret";
}
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 8312e932..957db868 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
@@ -29,11 +29,6 @@ public class ExtensionType
public static final int srp = 12;
/*
- * RFC 5077 7.
- */
- public static final int session_ticket = 35;
-
- /*
* RFC 5246 7.4.1.4.
*/
public static final int signature_algorithms = 13;
@@ -49,6 +44,30 @@ public class ExtensionType
public static final int heartbeat = 15;
/*
+ * RFC 7366
+ */
+ public static final int encrypt_then_mac = 22;
+
+ /*
+ * draft-ietf-tls-session-hash-01
+ *
+ * NOTE: Early code-point assignment
+ */
+ public static final int extended_master_secret = 23;
+
+ /*
+ * RFC 5077 7.
+ */
+ public static final int session_ticket = 35;
+
+ /*
+ * draft-ietf-tls-negotiated-ff-dhe-01
+ *
+ * WARNING: Placeholder value; the real value is TBA
+ */
+ public static final int negotiated_ff_dhe_groups = 101;
+
+ /*
* RFC 5746 3.2.
*/
public static final int renegotiation_info = 0xff01;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/FiniteFieldDHEGroup.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/FiniteFieldDHEGroup.java
new file mode 100644
index 00000000..a16ec9cc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/FiniteFieldDHEGroup.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.crypto.tls;
+
+/*
+ * draft-ietf-tls-negotiated-ff-dhe-01
+ */
+public class FiniteFieldDHEGroup
+{
+ public static final short ffdhe2432 = 0;
+ public static final short ffdhe3072 = 1;
+ public static final short ffdhe4096 = 2;
+ public static final short ffdhe6144 = 3;
+ public static final short ffdhe8192 = 4;
+
+ public static boolean isValid(short group)
+ {
+ return group >= ffdhe2432 && group <= ffdhe8192;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java
index 320a1282..c6a447fc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java
@@ -50,7 +50,7 @@ public class HeartbeatMessage
output.write(payload);
byte[] padding = new byte[paddingLength];
- context.getSecureRandom().nextBytes(padding);
+ context.getNonceRandomGenerator().nextBytes(padding);
output.write(padding);
}
@@ -87,6 +87,9 @@ public class HeartbeatMessage
int padding_length = buf.size() - payload.length;
+ /*
+ * RFC 6520 4. The padding of a received HeartbeatMessage message MUST be ignored
+ */
return new HeartbeatMessage(type, payload, padding_length);
}
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 72a944f8..d862d769 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
@@ -2,7 +2,7 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 2246
- * <p/>
+ * <p>
* Note that the values here are implementation-specific and arbitrary. It is recommended not to
* depend on the particular values (e.g. serialization).
*/
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsAuthentication.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsAuthentication.java
deleted file mode 100644
index f638714e..00000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsAuthentication.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.bouncycastle.crypto.tls;
-
-import java.io.IOException;
-
-/**
- * A temporary class to wrap old CertificateVerifyer stuff for new TlsAuthentication
- *
- * @deprecated
- */
-public class LegacyTlsAuthentication
- extends ServerOnlyTlsAuthentication
-{
- protected CertificateVerifyer verifyer;
-
- public LegacyTlsAuthentication(CertificateVerifyer verifyer)
- {
- this.verifyer = verifyer;
- }
-
- public void notifyServerCertificate(Certificate serverCertificate)
- throws IOException
- {
- if (!this.verifyer.isValid(serverCertificate.getCertificateList()))
- {
- throw new TlsFatalAlert(AlertDescription.user_canceled);
- }
- }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsClient.java
deleted file mode 100644
index 33217ac4..00000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/LegacyTlsClient.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.bouncycastle.crypto.tls;
-
-import java.io.IOException;
-
-/**
- * A temporary class to use LegacyTlsAuthentication
- *
- * @deprecated
- */
-public class LegacyTlsClient
- extends DefaultTlsClient
-{
- /**
- * @deprecated
- */
- protected CertificateVerifyer verifyer;
-
- /**
- * @deprecated
- */
- public LegacyTlsClient(CertificateVerifyer verifyer)
- {
- super();
-
- this.verifyer = verifyer;
- }
-
- public TlsAuthentication getAuthentication()
- throws IOException
- {
- return new LegacyTlsAuthentication(verifyer);
- }
-}
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 92adc8c2..bd13ab89 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
@@ -2,7 +2,7 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 2246
- * <p/>
+ * <p>
* Note that the values here are implementation-specific and arbitrary. It is recommended not to
* depend on the particular values (e.g. serialization).
*/
@@ -20,9 +20,4 @@ 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
index 413695cc..6bc61b0c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java
@@ -5,10 +5,10 @@ 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 final short pow2_9 = 1;
+ public static final short pow2_10 = 2;
+ public static final short pow2_11 = 3;
+ public static final short pow2_12 = 4;
public static boolean isValid(short maxFragmentLength)
{
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 a965d138..49fc923a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
@@ -2,7 +2,7 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 4492 5.1.1
- * <p/>
+ * <p>
* The named curves defined here are those specified in SEC 2 [13]. Note that many of these curves
* are also recommended in ANSI X9.62 [7] and FIPS 186-2 [11]. Values 0xFE00 through 0xFEFF are
* reserved for private use. Values 0xFF01 and 0xFF02 indicate that the client supports arbitrary
@@ -51,6 +51,12 @@ public class NamedCurve
public static final int arbitrary_explicit_prime_curves = 0xFF01;
public static final int arbitrary_explicit_char2_curves = 0xFF02;
+ public static boolean isValid(int namedCurve)
+ {
+ return (namedCurve >= sect163k1 && namedCurve <= brainpoolP512r1)
+ || (namedCurve >= arbitrary_explicit_prime_curves && namedCurve <= arbitrary_explicit_char2_curves);
+ }
+
public static boolean refersToASpecificNamedCurve(int namedCurve)
{
switch (namedCurve)
@@ -62,5 +68,4 @@ public class NamedCurve
return true;
}
}
-
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java
index db8168fc..473db23f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java
@@ -90,11 +90,11 @@ public class OCSPStatusRequest
}
/**
- * Parse a {@link OCSPStatusRequest} from an {@link InputStream}.
+ * Parse an {@link OCSPStatusRequest} from an {@link InputStream}.
*
* @param input
* the {@link InputStream} to parse from.
- * @return a {@link OCSPStatusRequest} object.
+ * @return an {@link OCSPStatusRequest} object.
* @throws IOException
*/
public static OCSPStatusRequest parse(InputStream input) throws IOException
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java
index 81a3bffb..33c27c4a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PRFAlgorithm.java
@@ -2,13 +2,12 @@ package org.bouncycastle.crypto.tls;
/**
* RFC 5246
- * <p/>
+ * <p>
* Note that the values here are implementation-specific and arbitrary. It is recommended not to
* depend on the particular values (e.g. serialization).
*/
public class PRFAlgorithm
{
-
/*
* Placeholder to refer to the legacy TLS algorithm
*/
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 92475b2d..cba58a96 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
@@ -2,15 +2,14 @@ package org.bouncycastle.crypto.tls;
import java.io.IOException;
-public abstract class PSKTlsClient
+public class PSKTlsClient
extends AbstractTlsClient
{
protected TlsPSKIdentity pskIdentity;
public PSKTlsClient(TlsPSKIdentity pskIdentity)
{
- super();
- this.pskIdentity = pskIdentity;
+ this(new DefaultTlsCipherFactory(), pskIdentity);
}
public PSKTlsClient(TlsCipherFactory cipherFactory, TlsPSKIdentity pskIdentity)
@@ -21,9 +20,13 @@ public abstract class PSKTlsClient
public int[] getCipherSuites()
{
- 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 };
+ return new int[]
+ {
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA
+ };
}
public TlsKeyExchange getKeyExchange() throws IOException
@@ -39,14 +42,16 @@ public abstract class PSKTlsClient
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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_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);
@@ -56,14 +61,14 @@ public abstract class PSKTlsClient
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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_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:
@@ -77,14 +82,16 @@ public abstract class PSKTlsClient
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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_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:
@@ -94,14 +101,16 @@ public abstract class PSKTlsClient
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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_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);
default:
@@ -114,6 +123,15 @@ public abstract class PSKTlsClient
}
}
+ public TlsAuthentication getAuthentication() throws IOException
+ {
+ /*
+ * Note: This method is not called unless a server certificate is sent, which may be the
+ * case e.g. for RSA_PSK key exchange.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
public TlsCipher getCipher() throws IOException
{
switch (selectedCipherSuite)
@@ -174,18 +192,34 @@ public abstract class PSKTlsClient
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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_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:
@@ -216,12 +250,6 @@ public abstract class PSKTlsClient
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
@@ -234,7 +262,7 @@ public abstract class PSKTlsClient
protected TlsKeyExchange createPSKKeyExchange(int keyExchange)
{
- return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity, null, namedCurves,
+ return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity, null, null, namedCurves,
clientECPointFormats, serverECPointFormats);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsServer.java
new file mode 100644
index 00000000..eedf025b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsServer.java
@@ -0,0 +1,346 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+
+import org.bouncycastle.crypto.agreement.DHStandardGroups;
+import org.bouncycastle.crypto.params.DHParameters;
+
+public class PSKTlsServer
+ extends AbstractTlsServer
+{
+ protected TlsPSKIdentityManager pskIdentityManager;
+
+ public PSKTlsServer(TlsPSKIdentityManager pskIdentityManager)
+ {
+ this(new DefaultTlsCipherFactory(), pskIdentityManager);
+ }
+
+ public PSKTlsServer(TlsCipherFactory cipherFactory, TlsPSKIdentityManager pskIdentityManager)
+ {
+ super(cipherFactory);
+ this.pskIdentityManager = pskIdentityManager;
+ }
+
+ protected TlsEncryptionCredentials getRSAEncryptionCredentials() throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected DHParameters getDHParameters()
+ {
+ return DHStandardGroups.rfc5114_1024_160;
+ }
+
+ protected int[] getCipherSuites()
+ {
+ return new int[]
+ {
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA
+ };
+ }
+
+ public TlsCredentials getCredentials() 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_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_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+
+ 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_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_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_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:
+ return null;
+
+ 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_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:
+ return getRSAEncryptionCredentials();
+
+ default:
+ /* Note: internal error here; selected a key exchange we don't implement! */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ 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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ 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_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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ 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:
+ 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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ 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:
+ 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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ 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:
+ return createPSKKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ 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:
+ 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:
+ 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:
+ 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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_CBC, MACAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_128_GCM, MACAlgorithm._null);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_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_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:
+ 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:
+ 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);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected TlsKeyExchange createPSKKeyExchange(int keyExchange)
+ {
+ return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, null, pskIdentityManager,
+ getDHParameters(), 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 b32bd9d3..8be2bec0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
@@ -2,6 +2,8 @@ package org.bouncycastle.crypto.tls;
import java.io.IOException;
+import org.bouncycastle.util.Strings;
+
public final class ProtocolVersion
{
public static final ProtocolVersion SSLv3 = new ProtocolVersion(0x0300, "SSL 3.0");
@@ -45,6 +47,11 @@ public final class ProtocolVersion
return this == SSLv3;
}
+ public boolean isTLS()
+ {
+ return getMajorVersion() == 0x03;
+ }
+
public ProtocolVersion getEquivalentTLSVersion()
{
if (!isDTLS())
@@ -78,9 +85,14 @@ public final class ProtocolVersion
return isDTLS() ? diffMinorVersion > 0 : diffMinorVersion < 0;
}
- public boolean equals(Object obj)
+ public boolean equals(Object other)
+ {
+ return this == other || (other instanceof ProtocolVersion && equals((ProtocolVersion)other));
+ }
+
+ public boolean equals(ProtocolVersion other)
{
- return this == obj;
+ return other != null && this.version == other.version;
}
public int hashCode()
@@ -94,6 +106,7 @@ public final class ProtocolVersion
switch (major)
{
case 0x03:
+ {
switch (minor)
{
case 0x00:
@@ -105,21 +118,41 @@ public final class ProtocolVersion
case 0x03:
return TLSv12;
}
+ return getUnknownVersion(major, minor, "TLS");
+ }
case 0xFE:
+ {
switch (minor)
{
case 0xFF:
return DTLSv10;
+ case 0xFE:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
case 0xFD:
return DTLSv12;
}
+ return getUnknownVersion(major, minor, "DTLS");
+ }
+ default:
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
}
-
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
public String toString()
{
return name;
}
+
+ private static ProtocolVersion getUnknownVersion(int major, int minor, String prefix)
+ throws IOException
+ {
+ TlsUtils.checkUint8(major);
+ TlsUtils.checkUint8(minor);
+
+ int v = (major << 8) | minor;
+ String hex = Strings.toUpperCase(Integer.toHexString(0x10000 | v).substring(1));
+ return new ProtocolVersion(v, prefix + " 0x" + hex);
+ }
}
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 cc6640be..5e28658b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
@@ -5,8 +5,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import org.bouncycastle.crypto.Digest;
-
/**
* An implementation of the TLS 1.0/1.1/1.2 record layer, allowing downgrade to SSLv3.
*/
@@ -22,7 +20,6 @@ class RecordStream
private long readSeqNo = 0, writeSeqNo = 0;
private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- private TlsContext context = null;
private TlsHandshakeHash handshakeHash = null;
private ProtocolVersion readVersion = null, writeVersion = null;
@@ -37,17 +34,16 @@ class RecordStream
this.output = output;
this.readCompression = new TlsNullCompression();
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.readCipher = new TlsNullCipher(context);
+ this.writeCipher = this.readCipher;
this.handshakeHash = new DeferredHash();
this.handshakeHash.init(context);
+
+ setPlaintextLimit(DEFAULT_PLAINTEXT_LIMIT);
}
int getPlaintextLimit()
@@ -127,11 +123,11 @@ class RecordStream
{
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
- pendingCompression = null;
- pendingCipher = null;
+ this.pendingCompression = null;
+ this.pendingCipher = null;
}
- public boolean readRecord()
+ boolean readRecord()
throws IOException
{
byte[] recordHeader = TlsUtils.readAllOrNothing(5, input);
@@ -175,7 +171,7 @@ class RecordStream
return true;
}
- protected byte[] decodeAndVerify(short type, InputStream input, int len)
+ byte[] decodeAndVerify(short type, InputStream input, int len)
throws IOException
{
checkLength(len, ciphertextLimit, AlertDescription.record_overflow);
@@ -216,9 +212,15 @@ class RecordStream
return decoded;
}
- protected void writeRecord(short type, byte[] plaintext, int plaintextOffset, int plaintextLength)
+ void writeRecord(short type, byte[] plaintext, int plaintextOffset, int plaintextLength)
throws IOException
{
+ // Never send anything until a valid ClientHello has been received
+ if (writeVersion == null)
+ {
+ return;
+ }
+
/*
* RFC 5264 6. Implementations MUST NOT send record types not defined in this document
* unless negotiated by some extension.
@@ -302,7 +304,7 @@ class RecordStream
handshakeHash.update(message, offset, len);
}
- protected void safeClose()
+ void safeClose()
{
try
{
@@ -321,7 +323,7 @@ class RecordStream
}
}
- protected void flush()
+ void flush()
throws IOException
{
output.flush();
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 15295ea8..15e8fe2b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
@@ -5,34 +5,45 @@ import java.util.Hashtable;
import org.bouncycastle.util.Arrays;
-public abstract class SRPTlsClient
+public class SRPTlsClient
extends AbstractTlsClient
{
- /**
- * @deprecated use TlsSRPUtils.EXT_SRP instead
- */
- public static final Integer EXT_SRP = TlsSRPUtils.EXT_SRP;
+ protected TlsSRPGroupVerifier groupVerifier;
protected byte[] identity;
protected byte[] password;
public SRPTlsClient(byte[] identity, byte[] password)
{
- super();
- this.identity = Arrays.clone(identity);
- this.password = Arrays.clone(password);
+ this(new DefaultTlsCipherFactory(), new DefaultTlsSRPGroupVerifier(), identity, password);
}
public SRPTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password)
{
+ this(cipherFactory, new DefaultTlsSRPGroupVerifier(), identity, password);
+ }
+
+ public SRPTlsClient(TlsCipherFactory cipherFactory, TlsSRPGroupVerifier groupVerifier,
+ byte[] identity, byte[] password)
+ {
super(cipherFactory);
+ this.groupVerifier = groupVerifier;
this.identity = Arrays.clone(identity);
this.password = Arrays.clone(password);
}
+ protected boolean requireSRPServerExtension()
+ {
+ // No explicit guidance in RFC 5054; by default an (empty) extension from server is optional
+ return false;
+ }
+
public int[] getCipherSuites()
{
- return new int[] { CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA };
+ return new int[]
+ {
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA
+ };
}
public Hashtable getClientExtensions()
@@ -46,16 +57,21 @@ public abstract class SRPTlsClient
public void processServerExtensions(Hashtable serverExtensions)
throws IOException
{
- if (!TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, TlsSRPUtils.EXT_SRP, AlertDescription.illegal_parameter))
+ if (!TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, TlsSRPUtils.EXT_SRP,
+ AlertDescription.illegal_parameter))
{
- // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
+ if (requireSRPServerExtension())
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
}
+
+ super.processServerExtensions(serverExtensions);
}
public TlsKeyExchange getKeyExchange()
throws IOException
{
-
switch (selectedCipherSuite)
{
case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
@@ -83,10 +99,18 @@ public abstract class SRPTlsClient
}
}
+ public TlsAuthentication getAuthentication() throws IOException
+ {
+ /*
+ * Note: This method is not called unless a server certificate is sent, which may be the
+ * case e.g. for SRP_DSS or SRP_RSA key exchange.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
public TlsCipher getCipher()
throws IOException
{
-
switch (selectedCipherSuite)
{
case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
@@ -116,6 +140,6 @@ public abstract class SRPTlsClient
protected TlsKeyExchange createSRPKeyExchange(int keyExchange)
{
- return new TlsSRPKeyExchange(keyExchange, supportedSignatureAlgorithms, identity, password);
+ return new TlsSRPKeyExchange(keyExchange, supportedSignatureAlgorithms, groupVerifier, identity, password);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsServer.java
new file mode 100644
index 00000000..bb8e39ab
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsServer.java
@@ -0,0 +1,166 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.util.Hashtable;
+
+public class SRPTlsServer
+ extends AbstractTlsServer
+{
+ protected TlsSRPIdentityManager srpIdentityManager;
+
+ protected byte[] srpIdentity = null;
+ protected TlsSRPLoginParameters loginParameters = null;
+
+ public SRPTlsServer(TlsSRPIdentityManager srpIdentityManager)
+ {
+ this(new DefaultTlsCipherFactory(), srpIdentityManager);
+ }
+
+ public SRPTlsServer(TlsCipherFactory cipherFactory, TlsSRPIdentityManager srpIdentityManager)
+ {
+ super(cipherFactory);
+ this.srpIdentityManager = srpIdentityManager;
+ }
+
+ protected TlsSignerCredentials getDSASignerCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected TlsSignerCredentials getRSASignerCredentials()
+ throws IOException
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected int[] getCipherSuites()
+ {
+ return new int[]
+ {
+ CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA
+ };
+ }
+
+ public void processClientExtensions(Hashtable clientExtensions) throws IOException
+ {
+ super.processClientExtensions(clientExtensions);
+
+ this.srpIdentity = TlsSRPUtils.getSRPExtension(clientExtensions);
+ }
+
+ public int getSelectedCipherSuite() throws IOException
+ {
+ int cipherSuite = super.getSelectedCipherSuite();
+
+ if (TlsSRPUtils.isSRPCipherSuite(cipherSuite))
+ {
+ if (srpIdentity != null)
+ {
+ this.loginParameters = srpIdentityManager.getLoginParameters(srpIdentity);
+ }
+
+ if (loginParameters == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unknown_psk_identity);
+ }
+ }
+
+ return cipherSuite;
+ }
+
+ public TlsCredentials getCredentials() throws IOException
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ return null;
+
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return getDSASignerCredentials();
+
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ return getRSASignerCredentials();
+
+ default:
+ /* Note: internal error here; selected a key exchange we don't implement! */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public TlsKeyExchange getKeyExchange()
+ throws IOException
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ return createSRPKeyExchange(KeyExchangeAlgorithm.SRP);
+
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ return createSRPKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
+
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return createSRPKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public TlsCipher getCipher()
+ throws IOException
+ {
+ switch (selectedCipherSuite)
+ {
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected TlsKeyExchange createSRPKeyExchange(int keyExchange)
+ {
+ return new TlsSRPKeyExchange(keyExchange, supportedSignatureAlgorithms, srpIdentity, loginParameters);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java
index 0d2e2f19..28082d2f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SSL3Mac.java
@@ -8,9 +8,9 @@ import org.bouncycastle.util.Arrays;
/**
* HMAC implementation based on original internet draft for HMAC (RFC 2104)
- * <p/>
+ * <p>
* The difference is that padding is concatenated versus XORed with the key
- * <p/>
+ * <p>
* H(K + opad, H(K + ipad, text))
*/
public class SSL3Mac
@@ -23,9 +23,9 @@ public class SSL3Mac
static final byte[] OPAD = genPad(OPAD_BYTE, 48);
private Digest digest;
+ private int padLength;
private byte[] secret;
- private int padLength;
/**
* Base constructor for one of the standard digest algorithms that the byteLength of
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 984246e1..27f76259 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
@@ -6,26 +6,21 @@ public class SecurityParameters
{
int entity = -1;
int cipherSuite = -1;
- short compressionAlgorithm = -1;
+ short compressionAlgorithm = CompressionMethod._null;
int prfAlgorithm = -1;
int verifyDataLength = -1;
byte[] masterSecret = null;
byte[] clientRandom = null;
byte[] serverRandom = null;
+ byte[] sessionHash = null;
+ byte[] pskIdentity = null;
+ byte[] srpIdentity = 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);
- }
+ boolean encryptThenMAC = false;
+ boolean extendedMasterSecret = false;
void clear()
{
@@ -87,4 +82,27 @@ public class SecurityParameters
{
return serverRandom;
}
+
+ public byte[] getSessionHash()
+ {
+ return sessionHash;
+ }
+
+ /**
+ * @deprecated Use {@link SecurityParameters#getPSKIdentity())
+ */
+ public byte[] getPskIdentity()
+ {
+ return pskIdentity;
+ }
+
+ public byte[] getPSKIdentity()
+ {
+ return pskIdentity;
+ }
+
+ public byte[] getSRPIdentity()
+ {
+ return srpIdentity;
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
index c3050f1b..911338ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
@@ -58,6 +58,6 @@ public class ServerDHParams
BigInteger g = TlsDHUtils.readDHParameter(input);
BigInteger Ys = TlsDHUtils.readDHParameter(input);
- return new ServerDHParams(new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+ return new ServerDHParams(TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Ys, new DHParameters(p, g))));
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerSRPParams.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerSRPParams.java
new file mode 100644
index 00000000..21ca7d0f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerSRPParams.java
@@ -0,0 +1,75 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+
+import org.bouncycastle.util.Arrays;
+
+public class ServerSRPParams
+{
+ protected BigInteger N, g, B;
+ protected byte[] s;
+
+ public ServerSRPParams(BigInteger N, BigInteger g, byte[] s, BigInteger B)
+ {
+ this.N = N;
+ this.g = g;
+ this.s = Arrays.clone(s);
+ this.B = B;
+ }
+
+ public BigInteger getB()
+ {
+ return B;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getN()
+ {
+ return N;
+ }
+
+ public byte[] getS()
+ {
+ return s;
+ }
+
+ /**
+ * Encode this {@link ServerSRPParams} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ TlsSRPUtils.writeSRPParameter(N, output);
+ TlsSRPUtils.writeSRPParameter(g, output);
+ TlsUtils.writeOpaque8(s, output);
+ TlsSRPUtils.writeSRPParameter(B, output);
+ }
+
+ /**
+ * Parse a {@link ServerSRPParams} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link ServerSRPParams} object.
+ * @throws IOException
+ */
+ public static ServerSRPParams parse(InputStream input) throws IOException
+ {
+ BigInteger N = TlsSRPUtils.readSRPParameter(input);
+ BigInteger g = TlsSRPUtils.readSRPParameter(input);
+ byte[] s = TlsUtils.readOpaque8(input);
+ BigInteger B = TlsSRPUtils.readSRPParameter(input);
+
+ return new ServerSRPParams(N, g, s, B);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
index 68412f84..c7418a02 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
@@ -15,6 +15,8 @@ public final class SessionParameters
private short compressionAlgorithm = -1;
private byte[] masterSecret = null;
private Certificate peerCertificate = null;
+ private byte[] pskIdentity = null;
+ private byte[] srpIdentity = null;
private byte[] encodedServerExtensions = null;
public Builder()
@@ -26,8 +28,8 @@ public final class SessionParameters
validate(this.cipherSuite >= 0, "cipherSuite");
validate(this.compressionAlgorithm >= 0, "compressionAlgorithm");
validate(this.masterSecret != null, "masterSecret");
- return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate,
- encodedServerExtensions);
+ return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate, pskIdentity,
+ srpIdentity, encodedServerExtensions);
}
public Builder setCipherSuite(int cipherSuite)
@@ -54,8 +56,28 @@ public final class SessionParameters
return this;
}
- public Builder setServerExtensions(Hashtable serverExtensions)
- throws IOException
+ /**
+ * @deprecated Use {@link #setPSKIdentity(byte[]))
+ */
+ public Builder setPskIdentity(byte[] pskIdentity)
+ {
+ this.pskIdentity = pskIdentity;
+ return this;
+ }
+
+ public Builder setPSKIdentity(byte[] pskIdentity)
+ {
+ this.pskIdentity = pskIdentity;
+ return this;
+ }
+
+ public Builder setSRPIdentity(byte[] srpIdentity)
+ {
+ this.srpIdentity = srpIdentity;
+ return this;
+ }
+
+ public Builder setServerExtensions(Hashtable serverExtensions) throws IOException
{
if (serverExtensions == null)
{
@@ -83,15 +105,19 @@ public final class SessionParameters
private short compressionAlgorithm;
private byte[] masterSecret;
private Certificate peerCertificate;
+ private byte[] pskIdentity = null;
+ private byte[] srpIdentity = null;
private byte[] encodedServerExtensions;
private SessionParameters(int cipherSuite, short compressionAlgorithm, byte[] masterSecret,
- Certificate peerCertificate, byte[] encodedServerExtensions)
+ Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions)
{
this.cipherSuite = cipherSuite;
this.compressionAlgorithm = compressionAlgorithm;
this.masterSecret = Arrays.clone(masterSecret);
this.peerCertificate = peerCertificate;
+ this.pskIdentity = Arrays.clone(pskIdentity);
+ this.srpIdentity = Arrays.clone(srpIdentity);
this.encodedServerExtensions = encodedServerExtensions;
}
@@ -105,8 +131,8 @@ public final class SessionParameters
public SessionParameters copy()
{
- return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate,
- encodedServerExtensions);
+ return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate, pskIdentity,
+ srpIdentity, encodedServerExtensions);
}
public int getCipherSuite()
@@ -129,6 +155,24 @@ public final class SessionParameters
return peerCertificate;
}
+ /**
+ * @deprecated Use {@link #getPSKIdentity())
+ */
+ public byte[] getPskIdentity()
+ {
+ return pskIdentity;
+ }
+
+ public byte[] getPSKIdentity()
+ {
+ return pskIdentity;
+ }
+
+ public byte[] getSRPIdentity()
+ {
+ return srpIdentity;
+ }
+
public Hashtable readServerExtensions() throws IOException
{
if (encodedServerExtensions == null)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java
index e63c7936..820c80cd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAlgorithm.java
@@ -5,7 +5,6 @@ package org.bouncycastle.crypto.tls;
*/
public class SignatureAlgorithm
{
-
public static final short anonymous = 0;
public static final short rsa = 1;
public static final short dsa = 2;
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 a5a591ee..9404a72f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
@@ -75,8 +75,8 @@ public class SignatureAndHashAlgorithm
public void encode(OutputStream output)
throws IOException
{
- TlsUtils.writeUint8(hash, output);
- TlsUtils.writeUint8(signature, output);
+ TlsUtils.writeUint8(getHash(), output);
+ TlsUtils.writeUint8(getSignature(), output);
}
/**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SimulatedTlsSRPIdentityManager.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SimulatedTlsSRPIdentityManager.java
new file mode 100644
index 00000000..c48b4ad9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SimulatedTlsSRPIdentityManager.java
@@ -0,0 +1,69 @@
+package org.bouncycastle.crypto.tls;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.agreement.srp.SRP6VerifierGenerator;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
+import org.bouncycastle.util.Strings;
+
+/**
+ * An implementation of {@link TlsSRPIdentityManager} that simulates the existence of "unknown" identities
+ * to obscure the fact that there is no verifier for them.
+ */
+public class SimulatedTlsSRPIdentityManager
+ implements TlsSRPIdentityManager
+{
+ private static final byte[] PREFIX_PASSWORD = Strings.toByteArray("password");
+ private static final byte[] PREFIX_SALT = Strings.toByteArray("salt");
+
+ /**
+ * Create a {@link SimulatedTlsSRPIdentityManager} that implements the algorithm from RFC 5054 2.5.1.3
+ *
+ * @param group the {@link SRP6GroupParameters} defining the group that SRP is operating in
+ * @param seedKey the secret "seed key" referred to in RFC 5054 2.5.1.3
+ * @return an instance of {@link SimulatedTlsSRPIdentityManager}
+ */
+ public static SimulatedTlsSRPIdentityManager getRFC5054Default(SRP6GroupParameters group, byte[] seedKey)
+ {
+ SRP6VerifierGenerator verifierGenerator = new SRP6VerifierGenerator();
+ verifierGenerator.init(group, TlsUtils.createHash(HashAlgorithm.sha1));
+
+ HMac mac = new HMac(TlsUtils.createHash(HashAlgorithm.sha1));
+ mac.init(new KeyParameter(seedKey));
+
+ return new SimulatedTlsSRPIdentityManager(group, verifierGenerator, mac);
+ }
+
+ protected SRP6GroupParameters group;
+ protected SRP6VerifierGenerator verifierGenerator;
+ protected Mac mac;
+
+ public SimulatedTlsSRPIdentityManager(SRP6GroupParameters group, SRP6VerifierGenerator verifierGenerator, Mac mac)
+ {
+ this.group = group;
+ this.verifierGenerator = verifierGenerator;
+ this.mac = mac;
+ }
+
+ public TlsSRPLoginParameters getLoginParameters(byte[] identity)
+ {
+ mac.update(PREFIX_SALT, 0, PREFIX_SALT.length);
+ mac.update(identity, 0, identity.length);
+
+ byte[] salt = new byte[mac.getMacSize()];
+ mac.doFinal(salt, 0);
+
+ mac.update(PREFIX_PASSWORD, 0, PREFIX_PASSWORD.length);
+ mac.update(identity, 0, identity.length);
+
+ byte[] password = new byte[mac.getMacSize()];
+ mac.doFinal(password, 0);
+
+ BigInteger verifier = verifierGenerator.generateVerifier(salt, identity, password);
+
+ return new TlsSRPLoginParameters(group, verifier, salt);
+ }
+}
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 bb9306a1..39133e6a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
@@ -120,7 +120,7 @@ public class TlsAEADCipher
}
catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
if (outputPos != output.length)
@@ -162,7 +162,7 @@ public class TlsAEADCipher
}
catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
}
if (outputPos != output.length)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java
index d8fe239c..525cdb38 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAgreementCredentials.java
@@ -7,7 +7,6 @@ import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
public interface TlsAgreementCredentials
extends TlsCredentials
{
-
byte[] generateAgreement(AsymmetricKeyParameter peerPublicKey)
throws IOException;
}
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 2f9e8a9a..c2c591c8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
@@ -11,16 +11,15 @@ import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
/**
- * A generic TLS 1.0-1.1 / SSLv3 block cipher. This can be used for AES or 3DES for example.
+ * A generic TLS 1.0-1.2 / SSLv3 block cipher. This can be used for AES or 3DES for example.
*/
public class TlsBlockCipher
implements TlsCipher
{
- private static boolean encryptThenMAC = false;
-
protected TlsContext context;
protected byte[] randomData;
protected boolean useExplicitIV;
+ protected boolean encryptThenMAC;
protected BlockCipher encryptCipher;
protected BlockCipher decryptCipher;
@@ -44,9 +43,10 @@ public class TlsBlockCipher
this.context = context;
this.randomData = new byte[256];
- context.getSecureRandom().nextBytes(randomData);
+ context.getNonceRandomGenerator().nextBytes(randomData);
this.useExplicitIV = TlsUtils.isTLSv11(context);
+ this.encryptThenMAC = context.getSecurityParameters().encryptThenMAC;
int key_block_size = (2 * cipherKeySize) + clientWriteDigest.getDigestSize()
+ serverWriteDigest.getDigestSize();
@@ -183,7 +183,7 @@ public class TlsBlockCipher
if (useExplicitIV)
{
byte[] explicitIV = new byte[blockSize];
- context.getSecureRandom().nextBytes(explicitIV);
+ context.getNonceRandomGenerator().nextBytes(explicitIV);
encryptCipher.init(true, new ParametersWithIV(null, explicitIV));
@@ -269,9 +269,16 @@ public class TlsBlockCipher
byte[] calculatedMac = readMac.calculateMac(seqNo, type, ciphertext, offset, len - macSize);
boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
-
if (badMac)
{
+ /*
+ * RFC 7366 3. The MAC SHALL be evaluated before any further processing such as
+ * decryption is performed, and if the MAC verification fails, then processing SHALL
+ * terminate immediately. For TLS, a fatal bad_record_mac MUST be generated [2]. For
+ * DTLS, the record MUST be discarded, and a fatal bad_record_mac MAY be generated
+ * [4]. This immediate response to a bad MAC eliminates any timing channels that may
+ * be available through the use of manipulated packet data.
+ */
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
}
@@ -291,6 +298,7 @@ public class TlsBlockCipher
// If there's anything wrong with the padding, this will return zero
int totalPad = checkPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMAC ? 0 : macSize);
+ boolean badMac = (totalPad == 0);
int dec_output_length = blocks_length - totalPad;
@@ -303,12 +311,12 @@ public class TlsBlockCipher
byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen,
blocks_length - macSize, randomData);
- boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
+ badMac |= !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
+ }
- if (badMac || totalPad == 0)
- {
- throw new TlsFatalAlert(AlertDescription.bad_record_mac);
- }
+ if (badMac)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
return Arrays.copyOfRange(ciphertext, offset, offset + dec_output_length);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java
index 29d961ff..74074747 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsCipherFactory.java
@@ -4,7 +4,6 @@ import java.io.IOException;
public interface TlsCipherFactory
{
-
/**
* See enumeration classes EncryptionAlgorithm, MACAlgorithm for appropriate argument values
*/
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 7db86cd4..da688b04 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
@@ -23,6 +23,8 @@ public interface TlsClient
ProtocolVersion getClientVersion();
+ boolean isFallback();
+
int[] getCipherSuites();
short[] getCompressionMethods();
@@ -36,9 +38,9 @@ public interface TlsClient
/**
* Notifies the client of the session_id sent in the ServerHello.
- *
+ *
* @param sessionID
- * @see {@link TlsContext#getResumableSession()}
+ * @see TlsContext#getResumableSession()
*/
void notifySessionID(byte[] sessionID);
@@ -66,7 +68,7 @@ public interface TlsClient
/**
* RFC 5077 3.3. NewSessionTicket Handshake Message
- * <p/>
+ * <p>
* This method will be called (only) when a NewSessionTicket handshake message is received. The
* ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption
* that it complies with e.g. <i>RFC 5077 4. Recommended Ticket Construction</i>.
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 506560fc..a776287a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
@@ -9,14 +9,13 @@ import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
-import org.bouncycastle.crypto.prng.ThreadedSeedGenerator;
import org.bouncycastle.util.Arrays;
public class TlsClientProtocol
extends TlsProtocol
{
protected TlsClient tlsClient = null;
- protected TlsClientContextImpl tlsClientContext = null;
+ TlsClientContextImpl tlsClientContext = null;
protected byte[] selectedSessionID = null;
@@ -26,28 +25,6 @@ public class TlsClientProtocol
protected CertificateStatus certificateStatus = null;
protected CertificateRequest certificateRequest = null;
- private static SecureRandom createSecureRandom()
- {
- /*
- * We use our threaded seed generator to generate a good random seed. If the user has a
- * better random seed, he should use the constructor with a SecureRandom.
- */
- ThreadedSeedGenerator tsg = new ThreadedSeedGenerator();
- SecureRandom random = new SecureRandom();
-
- /*
- * Hopefully, 20 bytes in fast mode are good enough.
- */
- random.setSeed(tsg.generateSeed(20, true));
-
- return random;
- }
-
- public TlsClientProtocol(InputStream input, OutputStream output)
- {
- this(input, output, createSecureRandom());
- }
-
public TlsClientProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
{
super(input, output, secureRandom);
@@ -74,9 +51,12 @@ public class TlsClientProtocol
this.securityParameters = new SecurityParameters();
this.securityParameters.entity = ConnectionEnd.client;
- this.securityParameters.clientRandom = createRandomBlock(secureRandom);
this.tlsClientContext = new TlsClientContextImpl(secureRandom, securityParameters);
+
+ this.securityParameters.clientRandom = createRandomBlock(tlsClient.shouldUseGMTUnixTime(),
+ tlsClientContext.getNonceRandomGenerator());
+
this.tlsClient.init(tlsClientContext);
this.recordStream.init(tlsClientContext);
@@ -108,11 +88,16 @@ public class TlsClientProtocol
this.certificateRequest = null;
}
- protected AbstractTlsContext getContext()
+ protected TlsContext getContext()
{
return tlsClientContext;
}
+ AbstractTlsContext getContextAdmin()
+ {
+ return tlsClientContext;
+ }
+
protected TlsPeer getPeer()
{
return tlsClient;
@@ -215,6 +200,19 @@ public class TlsClientProtocol
{
case CS_CLIENT_FINISHED:
{
+ if (this.expectSessionTicket)
+ {
+ /*
+ * RFC 5077 3.3. This message MUST be sent if the server included a
+ * SessionTicket extension in the ServerHello.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ // NB: Fall through to next case label
+ }
+ case CS_SERVER_SESSION_TICKET:
+ {
processFinishedMessage(buf);
this.connection_state = CS_SERVER_FINISHED;
this.connection_state = CS_END;
@@ -369,11 +367,12 @@ public class TlsClientProtocol
sendClientKeyExchangeMessage();
this.connection_state = CS_CLIENT_KEY_EXCHANGE;
+ TlsHandshakeHash prepareFinishHash = recordStream.prepareToFinish();
+ this.securityParameters.sessionHash = getCurrentPRFHash(getContext(), prepareFinishHash, null);
+
establishMasterSecret(getContext(), keyExchange);
recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
- TlsHandshakeHash prepareFinishHash = recordStream.prepareToFinish();
-
if (clientCreds != null && clientCreds instanceof TlsSignerCredentials)
{
TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds;
@@ -381,23 +380,17 @@ public class TlsClientProtocol
/*
* RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
*/
- SignatureAndHashAlgorithm signatureAndHashAlgorithm;
- byte[] hash;
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtils.getSignatureAndHashAlgorithm(
+ getContext(), signerCredentials);
- if (TlsUtils.isTLSv12(getContext()))
+ byte[] hash;
+ if (signatureAndHashAlgorithm == null)
{
- signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm();
- if (signatureAndHashAlgorithm == null)
- {
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
-
- hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash());
+ hash = securityParameters.getSessionHash();
}
else
{
- signatureAndHashAlgorithm = null;
- hash = getCurrentPRFHash(getContext(), prepareFinishHash, null);
+ hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash());
}
byte[] signature = signerCredentials.generateCertificateSignature(hash);
@@ -409,12 +402,13 @@ public class TlsClientProtocol
sendChangeCipherSpecMessage();
sendFinishedMessage();
- this.connection_state = CS_CLIENT_FINISHED;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
+
+ this.connection_state = CS_CLIENT_FINISHED;
break;
}
case HandshakeType.server_key_exchange:
@@ -516,12 +510,14 @@ public class TlsClientProtocol
invalidateSession();
receiveNewSessionTicketMessage(buf);
- this.connection_state = CS_SERVER_SESSION_TICKET;
break;
}
default:
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
+
+ this.connection_state = CS_SERVER_SESSION_TICKET;
+ break;
}
case HandshakeType.hello_request:
{
@@ -573,7 +569,7 @@ public class TlsClientProtocol
{
NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf);
- TlsProtocol.assertEmpty(buf);
+ assertEmpty(buf);
tlsClient.notifyNewSessionTicket(newSessionTicket);
}
@@ -600,7 +596,7 @@ public class TlsClientProtocol
}
this.recordStream.setWriteVersion(server_version);
- getContext().setServerVersion(server_version);
+ getContextAdmin().setServerVersion(server_version);
this.tlsClient.notifyServerVersion(server_version);
/*
@@ -621,12 +617,13 @@ public class TlsClientProtocol
/*
* Find out which CipherSuite the server has chosen and check that it was one of the offered
- * ones.
+ * ones, and is a valid selection for the negotiated version.
*/
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)
+ || CipherSuite.isSCSV(selectedCipherSuite)
+ || !TlsUtils.isValidCipherSuiteForVersion(selectedCipherSuite, server_version))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
@@ -656,6 +653,17 @@ public class TlsClientProtocol
this.serverExtensions = readExtensions(buf);
/*
+ * draft-ietf-tls-session-hash-01 5.2. If a server receives the "extended_master_secret"
+ * extension, it MUST include the "extended_master_secret" extension in its ServerHello
+ * message.
+ */
+ boolean serverSentExtendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(serverExtensions);
+ if (serverSentExtendedMasterSecret != securityParameters.extendedMasterSecret)
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ /*
* RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
* extended client hello message.
*
@@ -682,6 +690,29 @@ public class TlsClientProtocol
}
/*
+ * 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);
+ }
+
+ /*
+ * draft-ietf-tls-session-hash-01 5.2. Implementation note: if the server decides to
+ * proceed with resumption, the extension does not have any effect. Requiring the
+ * extension to be included anyway makes the extension negotiation logic easier,
+ * because it does not depend on whether resumption is accepted or not.
+ */
+ if (extType.equals(TlsExtensionsUtils.EXT_extended_master_secret))
+ {
+ 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[.]
@@ -693,18 +724,6 @@ public class TlsClientProtocol
// 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);
- }
}
}
@@ -748,6 +767,8 @@ public class TlsClientProtocol
sessionClientExtensions = null;
sessionServerExtensions = this.sessionParameters.readServerExtensions();
+
+ this.securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(sessionServerExtensions);
}
this.securityParameters.cipherSuite = selectedCipherSuite;
@@ -755,6 +776,20 @@ public class TlsClientProtocol
if (sessionServerExtensions != null)
{
+ /*
+ * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
+ * and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
+ * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
+ * client.
+ */
+ boolean serverSentEncryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(sessionServerExtensions);
+ if (serverSentEncryptThenMAC && !TlsUtils.isBlockCipherSuite(selectedCipherSuite))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.securityParameters.encryptThenMAC = serverSentEncryptThenMAC;
+
this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(sessionClientExtensions,
sessionServerExtensions, AlertDescription.illegal_parameter);
@@ -800,7 +835,7 @@ public class TlsClientProtocol
throw new TlsFatalAlert(AlertDescription.internal_error);
}
- getContext().setClientVersion(client_version);
+ getContextAdmin().setClientVersion(client_version);
/*
* TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a
@@ -816,6 +851,8 @@ public class TlsClientProtocol
}
}
+ boolean fallback = this.tlsClient.isFallback();
+
this.offeredCipherSuites = this.tlsClient.getCipherSuites();
this.offeredCompressionMethods = this.tlsClient.getCompressionMethods();
@@ -831,6 +868,8 @@ public class TlsClientProtocol
this.clientExtensions = this.tlsClient.getClientExtensions();
+ this.securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(clientExtensions);
+
HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
TlsUtils.writeVersion(client_version, message);
@@ -849,9 +888,9 @@ public class TlsClientProtocol
byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo);
boolean noRenegExt = (null == renegExtData);
- boolean noSCSV = !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+ boolean noRenegSCSV = !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
- if (noRenegExt && noSCSV)
+ if (noRenegExt && noRenegSCSV)
{
// TODO Consider whether to default to a client extension instead
// this.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.clientExtensions);
@@ -859,6 +898,17 @@ public class TlsClientProtocol
this.offeredCipherSuites = Arrays.append(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
}
+ /*
+ * draft-ietf-tls-downgrade-scsv-00 4. If a client sends a ClientHello.client_version
+ * containing a lower value than the latest (highest-valued) version supported by the
+ * client, it SHOULD include the TLS_FALLBACK_SCSV cipher suite value in
+ * ClientHello.cipher_suites.
+ */
+ if (fallback && !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
+ {
+ this.offeredCipherSuites = Arrays.append(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
+ }
+
TlsUtils.writeUint16ArrayWithUint16Length(offeredCipherSuites, message);
}
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 04781efc..b3d7d985 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
@@ -2,8 +2,12 @@ package org.bouncycastle.crypto.tls;
import java.security.SecureRandom;
+import org.bouncycastle.crypto.prng.RandomGenerator;
+
public interface TlsContext
{
+ RandomGenerator getNonceRandomGenerator();
+
SecureRandom getSecureRandom();
SecurityParameters getSecurityParameters();
@@ -20,7 +24,7 @@ public interface TlsContext
*
* @return A {@link TlsSession} representing the resumable session used by this connection, or
* null if no resumable session available.
- * @see {@link TlsPeer#notifyHandshakeComplete()}
+ * @see TlsPeer#notifyHandshakeComplete()
*/
TlsSession getResumableSession();
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 0abaee69..d469ca6d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
@@ -42,30 +42,16 @@ public class TlsDHEKeyExchange
DigestInputBuffer buf = new DigestInputBuffer();
- this.dhAgreeServerPrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(),
+ this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(),
this.dhParameters, buf);
/*
* RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
*/
- SignatureAndHashAlgorithm signatureAndHashAlgorithm;
- Digest d;
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtils.getSignatureAndHashAlgorithm(
+ context, serverCredentials);
- if (TlsUtils.isTLSv12(context))
- {
- signatureAndHashAlgorithm = serverCredentials.getSignatureAndHashAlgorithm();
- if (signatureAndHashAlgorithm == null)
- {
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
-
- d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash());
- }
- else
- {
- signatureAndHashAlgorithm = null;
- d = new CombinedHash();
- }
+ Digest d = TlsUtils.createHash(signatureAndHashAlgorithm);
SecurityParameters securityParameters = context.getSecurityParameters();
d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
@@ -91,7 +77,7 @@ public class TlsDHEKeyExchange
SignerInputBuffer buf = new SignerInputBuffer();
InputStream teeIn = new TeeInputStream(input, buf);
- ServerDHParams params = ServerDHParams.parse(teeIn);
+ ServerDHParams dhParams = ServerDHParams.parse(teeIn);
DigitallySigned signed_params = DigitallySigned.parse(context, input);
@@ -102,7 +88,8 @@ public class TlsDHEKeyExchange
throw new TlsFatalAlert(AlertDescription.decrypt_error);
}
- this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(params.getPublicKey());
+ this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(dhParams.getPublicKey());
+ this.dhParameters = dhAgreePublicKey.getParameters();
}
protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
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 7d79f6a4..2662fdac 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
@@ -1,6 +1,7 @@
package org.bouncycastle.crypto.tls;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Vector;
@@ -14,24 +15,19 @@ import org.bouncycastle.crypto.params.DHPublicKeyParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
/**
- * TLS 1.0/1.1 DH key exchange.
+ * (D)TLS DH key exchange.
*/
public class TlsDHKeyExchange
extends AbstractTlsKeyExchange
{
- protected static final BigInteger ONE = BigInteger.valueOf(1);
- protected static final BigInteger TWO = BigInteger.valueOf(2);
-
protected TlsSigner tlsSigner;
protected DHParameters dhParameters;
protected AsymmetricKeyParameter serverPublicKey;
- protected DHPublicKeyParameters dhAgreeServerPublicKey;
protected TlsAgreementCredentials agreementCredentials;
- protected DHPrivateKeyParameters dhAgreeClientPrivateKey;
- protected DHPrivateKeyParameters dhAgreeServerPrivateKey;
- protected DHPublicKeyParameters dhAgreeClientPublicKey;
+ protected DHPrivateKeyParameters dhAgreePrivateKey;
+ protected DHPublicKeyParameters dhAgreePublicKey;
public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
{
@@ -89,18 +85,18 @@ public class TlsDHKeyExchange
}
catch (RuntimeException e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
if (tlsSigner == null)
{
try
{
- this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
+ this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
}
catch (ClassCastException e)
{
- throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
}
TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyAgreement);
@@ -180,27 +176,41 @@ public class TlsDHKeyExchange
*/
if (agreementCredentials == null)
{
- this.dhAgreeClientPrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
- dhAgreeServerPublicKey.getParameters(), output);
+ this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
+ dhParameters, output);
}
}
- public byte[] generatePremasterSecret()
- throws IOException
+ public void processClientCertificate(Certificate clientCertificate) throws IOException
{
- if (agreementCredentials != null)
+ // TODO Extract the public key
+ // TODO If the certificate is 'fixed', take the public key as dhAgreeClientPublicKey
+ }
+
+ public void processClientKeyExchange(InputStream input) throws IOException
+ {
+ if (dhAgreePublicKey != null)
{
- return agreementCredentials.generateAgreement(dhAgreeServerPublicKey);
+ // For dss_fixed_dh and rsa_fixed_dh, the key arrived in the client certificate
+ return;
}
- if (dhAgreeServerPrivateKey != null)
+ BigInteger Yc = TlsDHUtils.readDHParameter(input);
+
+ this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Yc, dhParameters));
+ }
+
+ public byte[] generatePremasterSecret()
+ throws IOException
+ {
+ if (agreementCredentials != null)
{
- return TlsDHUtils.calculateDHBasicAgreement(dhAgreeClientPublicKey, dhAgreeServerPrivateKey);
+ return agreementCredentials.generateAgreement(dhAgreePublicKey);
}
- if (dhAgreeClientPrivateKey != null)
+ if (dhAgreePrivateKey != null)
{
- return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+ return TlsDHUtils.calculateDHBasicAgreement(dhAgreePublicKey, dhAgreePrivateKey);
}
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 748c8797..eef27a44 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
@@ -1,10 +1,12 @@
package org.bouncycastle.crypto.tls;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
+import java.util.Hashtable;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.agreement.DHBasicAgreement;
@@ -14,12 +16,405 @@ import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
import org.bouncycastle.crypto.params.DHPublicKeyParameters;
import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.Integers;
+import org.bouncycastle.util.encoders.Hex;
public class TlsDHUtils
{
- static final BigInteger ONE = BigInteger.valueOf(1);
static final BigInteger TWO = BigInteger.valueOf(2);
+ public static final Integer EXT_negotiated_ff_dhe_groups = Integers.valueOf(ExtensionType.negotiated_ff_dhe_groups);
+
+ /*
+ * TODO[draft-ietf-tls-negotiated-ff-dhe-01] Move these groups to DHStandardGroups once reaches RFC
+ */
+ private static BigInteger fromHex(String hex)
+ {
+ return new BigInteger(1, Hex.decode(hex));
+ }
+
+ private static DHParameters fromSafeP(String hexP)
+ {
+ BigInteger p = fromHex(hexP), q = p.shiftRight(1);
+ return new DHParameters(p, TWO, q);
+ }
+
+ private static final String draft_ffdhe2432_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE13098533C8B3FFFFFFFFFFFFFFFF";
+ static final DHParameters draft_ffdhe2432 = fromSafeP(draft_ffdhe2432_p);
+
+ private static final String draft_ffdhe3072_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D"
+ + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF";
+ static final DHParameters draft_ffdhe3072 = fromSafeP(draft_ffdhe3072_p);
+
+ private static final String draft_ffdhe4096_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D"
+ + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB"
+ + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+ + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832"
+ + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+ + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF"
+ + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A"
+ + "FFFFFFFFFFFFFFFF";
+ static final DHParameters draft_ffdhe4096 = fromSafeP(draft_ffdhe4096_p);
+
+ private static final String draft_ffdhe6144_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D"
+ + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB"
+ + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+ + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832"
+ + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+ + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF"
+ + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902"
+ + "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6"
+ + "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A"
+ + "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477"
+ + "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3"
+ + "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4"
+ + "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6"
+ + "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C"
+ + "D72B03746AE77F5E62292C311562A846505DC82DB854338A"
+ + "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04"
+ + "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1"
+ + "A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF";
+ static final DHParameters draft_ffdhe6144 = fromSafeP(draft_ffdhe6144_p);
+
+ private static final String draft_ffdhe8192_p =
+ "FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1"
+ + "D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9"
+ + "7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561"
+ + "2433F51F5F066ED0856365553DED1AF3B557135E7F57C935"
+ + "984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735"
+ + "30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB"
+ + "B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19"
+ + "0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61"
+ + "9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73"
+ + "3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA"
+ + "886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238"
+ + "61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C"
+ + "AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3"
+ + "64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D"
+ + "ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF"
+ + "3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB"
+ + "7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004"
+ + "87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832"
+ + "A907600A918130C46DC778F971AD0038092999A333CB8B7A"
+ + "1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF"
+ + "8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902"
+ + "0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6"
+ + "3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A"
+ + "CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477"
+ + "A52471F7A9A96910B855322EDB6340D8A00EF092350511E3"
+ + "0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4"
+ + "763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6"
+ + "B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C"
+ + "D72B03746AE77F5E62292C311562A846505DC82DB854338A"
+ + "E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04"
+ + "5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1"
+ + "A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C838"
+ + "1E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E"
+ + "0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665"
+ + "CB2C0F1CC01BD70229388839D2AF05E454504AC78B758282"
+ + "2846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022"
+ + "BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C"
+ + "51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9"
+ + "D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA457"
+ + "1EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30"
+ + "FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D"
+ + "97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88C"
+ + "D68C8BB7C5C6424CFFFFFFFFFFFFFFFF";
+ static final DHParameters draft_ffdhe8192 = fromSafeP(draft_ffdhe8192_p);
+
+
+ public static void addNegotiatedDHEGroupsClientExtension(Hashtable extensions, short[] dheGroups)
+ throws IOException
+ {
+ extensions.put(EXT_negotiated_ff_dhe_groups, createNegotiatedDHEGroupsClientExtension(dheGroups));
+ }
+
+ public static void addNegotiatedDHEGroupsServerExtension(Hashtable extensions, short dheGroup)
+ throws IOException
+ {
+ extensions.put(EXT_negotiated_ff_dhe_groups, createNegotiatedDHEGroupsServerExtension(dheGroup));
+ }
+
+ public static short[] getNegotiatedDHEGroupsClientExtension(Hashtable extensions) throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_negotiated_ff_dhe_groups);
+ return extensionData == null ? null : readNegotiatedDHEGroupsClientExtension(extensionData);
+ }
+
+ public static short getNegotiatedDHEGroupsServerExtension(Hashtable extensions) throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_negotiated_ff_dhe_groups);
+ return extensionData == null ? -1 : readNegotiatedDHEGroupsServerExtension(extensionData);
+ }
+
+ public static byte[] createNegotiatedDHEGroupsClientExtension(short[] dheGroups) throws IOException
+ {
+ if (dheGroups == null || dheGroups.length < 1 || dheGroups.length > 255)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return TlsUtils.encodeUint8ArrayWithUint8Length(dheGroups);
+ }
+
+ public static byte[] createNegotiatedDHEGroupsServerExtension(short dheGroup) throws IOException
+ {
+ TlsUtils.checkUint8(dheGroup);
+
+ byte[] extensionData = new byte[1];
+ TlsUtils.writeUint8(dheGroup, extensionData, 0);
+ return extensionData;
+ }
+
+ public static short[] readNegotiatedDHEGroupsClientExtension(byte[] extensionData) throws IOException
+ {
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+
+ short length = TlsUtils.readUint8(buf);
+ if (length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ short[] dheGroups = TlsUtils.readUint8Array(length, buf);
+
+ TlsProtocol.assertEmpty(buf);
+
+ return dheGroups;
+ }
+
+ public static short readNegotiatedDHEGroupsServerExtension(byte[] extensionData) throws IOException
+ {
+ if (extensionData == null)
+ {
+ throw new IllegalArgumentException("'extensionData' cannot be null");
+ }
+
+ if (extensionData.length != 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return TlsUtils.readUint8(extensionData, 0);
+ }
+
+ public static DHParameters getParametersForDHEGroup(short dheGroup)
+ {
+ switch (dheGroup)
+ {
+ case FiniteFieldDHEGroup.ffdhe2432:
+ return draft_ffdhe2432;
+ case FiniteFieldDHEGroup.ffdhe3072:
+ return draft_ffdhe3072;
+ case FiniteFieldDHEGroup.ffdhe4096:
+ return draft_ffdhe4096;
+ case FiniteFieldDHEGroup.ffdhe6144:
+ return draft_ffdhe6144;
+ case FiniteFieldDHEGroup.ffdhe8192:
+ return draft_ffdhe8192;
+ default:
+ return null;
+ }
+ }
+
+ public static boolean containsDHECipherSuites(int[] cipherSuites)
+ {
+ for (int i = 0; i < cipherSuites.length; ++i)
+ {
+ if (isDHECipherSuite(cipherSuites[i]))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isDHECipherSuite(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ /*
+ * RFC 2246
+ */
+ case CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+
+ /*
+ * RFC 3268
+ */
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+
+ /*
+ * RFC 5932
+ */
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+
+ /*
+ * RFC 4162
+ */
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+
+ /*
+ * RFC 4279
+ */
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ 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:
+
+ /*
+ * RFC 4785
+ */
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+
+ /*
+ * RFC 5246
+ */
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+
+ /*
+ * RFC 5288
+ */
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+
+ /*
+ * RFC 5487
+ */
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+
+ /*
+ * RFC 6367
+ */
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+
+ /*
+ * RFC 6655
+ */
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+
+ /*
+ * draft-agl-tls-chacha20poly1305-04
+ */
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+
+ /*
+ * draft-josefsson-salsa20-tls-04
+ */
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
public static boolean areCompatibleParameters(DHParameters a, DHParameters b)
{
return a.getP().equals(b.getP()) && a.getG().equals(b.getG());
@@ -50,8 +445,8 @@ public class TlsDHUtils
{
AsymmetricCipherKeyPair kp = generateDHKeyPair(random, dhParams);
- DHPublicKeyParameters dh_public = (DHPublicKeyParameters) kp.getPublic();
- writeDHParameter(dh_public.getY(), output);
+ DHPublicKeyParameters dhPublic = (DHPublicKeyParameters) kp.getPublic();
+ writeDHParameter(dhPublic.getY(), output);
return (DHPrivateKeyParameters) kp.getPrivate();
}
@@ -59,11 +454,10 @@ public class TlsDHUtils
public static DHPrivateKeyParameters generateEphemeralServerKeyExchange(SecureRandom random, DHParameters dhParams,
OutputStream output) throws IOException
{
- AsymmetricCipherKeyPair kp = TlsDHUtils.generateDHKeyPair(random, dhParams);
+ AsymmetricCipherKeyPair kp = generateDHKeyPair(random, dhParams);
- DHPublicKeyParameters dhPublicKey = (DHPublicKeyParameters)kp.getPublic();
- ServerDHParams params = new ServerDHParams(dhPublicKey);
- params.encode(output);
+ DHPublicKeyParameters dhPublic = (DHPublicKeyParameters)kp.getPublic();
+ new ServerDHParams(dhPublic).encode(output);
return (DHPrivateKeyParameters)kp.getPrivate();
}
@@ -83,7 +477,7 @@ public class TlsDHUtils
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- if (Y.compareTo(TWO) < 0 || Y.compareTo(p.subtract(ONE)) > 0)
+ if (Y.compareTo(TWO) < 0 || Y.compareTo(p.subtract(TWO)) > 0)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
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 4cb80040..7d068bfb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
@@ -50,7 +50,7 @@ public abstract class TlsDSASigner
public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey)
{
- return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+ return makeSigner(algorithm, false, true, privateKey);
}
public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey)
@@ -58,6 +58,11 @@ public abstract class TlsDSASigner
return makeSigner(algorithm, false, false, publicKey);
}
+ protected CipherParameters makeInitParameters(boolean forSigning, CipherParameters cp)
+ {
+ return cp;
+ }
+
protected Signer makeSigner(SignatureAndHashAlgorithm algorithm, boolean raw, boolean forSigning,
CipherParameters cp)
{
@@ -66,20 +71,20 @@ public abstract class TlsDSASigner
throw new IllegalStateException();
}
- if (algorithm != null
- && (algorithm.getHash() != HashAlgorithm.sha1 || algorithm.getSignature() != getSignatureAlgorithm()))
+ if (algorithm != null && algorithm.getSignature() != getSignatureAlgorithm())
{
throw new IllegalStateException();
}
- Digest d = raw ? new NullDigest() : TlsUtils.createHash(HashAlgorithm.sha1);
+ short hashAlgorithm = algorithm == null ? HashAlgorithm.sha1 : algorithm.getHash();
+ Digest d = raw ? new NullDigest() : TlsUtils.createHash(hashAlgorithm);
- Signer s = new DSADigestSigner(createDSAImpl(), d);
- s.init(forSigning, cp);
+ Signer s = new DSADigestSigner(createDSAImpl(hashAlgorithm), d);
+ s.init(forSigning, makeInitParameters(forSigning, cp));
return s;
}
protected abstract short getSignatureAlgorithm();
- protected abstract DSA createDSAImpl();
+ protected abstract DSA createDSAImpl(short hashAlgorithm);
}
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 cb698bf4..2914b7e4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
@@ -4,6 +4,7 @@ import org.bouncycastle.crypto.DSA;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.signers.DSASigner;
+import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
public class TlsDSSSigner
extends TlsDSASigner
@@ -13,9 +14,9 @@ public class TlsDSSSigner
return publicKey instanceof DSAPublicKeyParameters;
}
- protected DSA createDSAImpl()
+ protected DSA createDSAImpl(short hashAlgorithm)
{
- return new DSASigner();
+ return new DSASigner(new HMacDSAKCalculator(TlsUtils.createHash(hashAlgorithm)));
}
protected short getSignatureAlgorithm()
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 3e7ef39d..de562ea2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
@@ -12,14 +12,17 @@ import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.math.field.PolynomialExtensionField;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Integers;
@@ -29,7 +32,7 @@ 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[] CURVE_NAMES = 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",
@@ -70,11 +73,7 @@ public class TlsECCUtils
public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats) throws IOException
{
- if (ecPointFormats == null)
- {
- ecPointFormats = new short[] { ECPointFormat.uncompressed };
- }
- else if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed))
+ if (ecPointFormats == null || !Arrays.contains(ecPointFormats, ECPointFormat.uncompressed))
{
/*
* RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
@@ -82,11 +81,7 @@ public class TlsECCUtils
*/
// NOTE: We add it at the end (lowest preference)
- short[] tmp = new short[ecPointFormats.length + 1];
- System.arraycopy(ecPointFormats, 0, tmp, 0, ecPointFormats.length);
- tmp[ecPointFormats.length] = ECPointFormat.uncompressed;
-
- ecPointFormats = tmp;
+ ecPointFormats = Arrays.append(ecPointFormats, ECPointFormat.uncompressed);
}
return TlsUtils.encodeUint8ArrayWithUint8Length(ecPointFormats);
@@ -147,7 +142,7 @@ public class TlsECCUtils
public static String getNameOfNamedCurve(int namedCurve)
{
- return isSupportedNamedCurve(namedCurve) ? curveNames[namedCurve - 1] : null;
+ return isSupportedNamedCurve(namedCurve) ? CURVE_NAMES[namedCurve - 1] : null;
}
public static ECDomainParameters getParametersForNamedCurve(int namedCurve)
@@ -158,12 +153,16 @@ public class TlsECCUtils
return null;
}
- // Lazily created the first time a particular curve is accessed
- X9ECParameters ecP = ECNamedCurveTable.getByName(curveName);
+ // Parameters are lazily created the first time a particular curve is accessed
+ X9ECParameters ecP = CustomNamedCurves.getByName(curveName);
if (ecP == null)
{
- return null;
+ ecP = ECNamedCurveTable.getByName(curveName);
+ if (ecP == null)
+ {
+ return null;
+ }
}
// It's a bit inefficient to do this conversion every time
@@ -172,7 +171,7 @@ public class TlsECCUtils
public static boolean hasAnySupportedNamedCurves()
{
- return curveNames.length > 0;
+ return CURVE_NAMES.length > 0;
}
public static boolean containsECCCipherSuites(int[] cipherSuites)
@@ -254,20 +253,52 @@ public class TlsECCUtils
case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
/*
- * draft-josefsson-salsa20-tls-02
+ * RFC 6367
+ */
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+
+ /*
+ * RFC 7251
+ */
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
+
+ /*
+ * draft-agl-tls-chacha20poly1305-04
+ */
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+
+ /*
+ * draft-josefsson-salsa20-tls-04
*/
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;
@@ -285,7 +316,7 @@ public class TlsECCUtils
public static boolean isSupportedNamedCurve(int namedCurve)
{
- return (namedCurve > 0 && namedCurve <= curveNames.length);
+ return (namedCurve > 0 && namedCurve <= CURVE_NAMES.length);
}
public static boolean isCompressionPreferred(short[] ecPointFormats, short compressionFormat)
@@ -325,13 +356,13 @@ public class TlsECCUtils
* used.
*/
boolean compressed = false;
- if (curve instanceof ECCurve.F2m)
+ if (ECAlgorithms.isFpCurve(curve))
{
- compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2);
+ compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime);
}
- else if (curve instanceof ECCurve.Fp)
+ else if (ECAlgorithms.isF2mCurve(curve))
{
- compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime);
+ compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2);
}
return point.getEncoded(compressed);
}
@@ -354,12 +385,49 @@ public class TlsECCUtils
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
+ if (encoding == null || encoding.length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ short actualFormat;
+ switch (encoding[0])
+ {
+ case 0x02: // compressed
+ case 0x03: // compressed
+ {
+ if (ECAlgorithms.isF2mCurve(curve))
+ {
+ actualFormat = ECPointFormat.ansiX962_compressed_char2;
+ }
+ else if (ECAlgorithms.isFpCurve(curve))
+ {
+ actualFormat = ECPointFormat.ansiX962_compressed_prime;
+ }
+ else
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ break;
+ }
+ case 0x04: // uncompressed
+ {
+ actualFormat = ECPointFormat.uncompressed;
+ break;
+ }
+ case 0x00: // infinity
+ case 0x06: // hybrid
+ case 0x07: // hybrid
+ default:
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ if (actualFormat != ECPointFormat.uncompressed
+ && (ecPointFormats == null || !Arrays.contains(ecPointFormats, actualFormat)))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
return curve.decodePoint(encoding);
}
@@ -373,7 +441,7 @@ public class TlsECCUtils
}
catch (RuntimeException e)
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
@@ -401,7 +469,7 @@ public class TlsECCUtils
public static ECPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random, short[] ecPointFormats,
ECDomainParameters ecParams, OutputStream output) throws IOException
{
- AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(random, ecParams);
+ AsymmetricCipherKeyPair kp = generateECKeyPair(random, ecParams);
ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic();
writeECPoint(ecPointFormats, ecPublicKey.getQ(), output);
@@ -409,6 +477,69 @@ public class TlsECCUtils
return (ECPrivateKeyParameters) kp.getPrivate();
}
+ // TODO Refactor around ServerECDHParams before making this public
+ static ECPrivateKeyParameters generateEphemeralServerKeyExchange(SecureRandom random, int[] namedCurves,
+ short[] ecPointFormats, OutputStream output) 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
+ {
+ for (int i = 0; i < namedCurves.length; ++i)
+ {
+ int entry = namedCurves[i];
+ if (NamedCurve.isValid(entry) && isSupportedNamedCurve(entry))
+ {
+ namedCurve = entry;
+ break;
+ }
+ }
+ }
+
+ ECDomainParameters ecParams = null;
+ if (namedCurve >= 0)
+ {
+ ecParams = getParametersForNamedCurve(namedCurve);
+ }
+ else
+ {
+ /* If no named curves are suitable, check if the client supports explicit curves. */
+ if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
+ {
+ ecParams = getParametersForNamedCurve(NamedCurve.secp256r1);
+ }
+ else if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
+ {
+ ecParams = getParametersForNamedCurve(NamedCurve.sect283r1);
+ }
+ }
+
+ if (ecParams == null)
+ {
+ /*
+ * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find
+ * a suitable curve.
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ if (namedCurve < 0)
+ {
+ writeExplicitECParameters(ecPointFormats, ecParams, output);
+ }
+ else
+ {
+ writeNamedECParameters(namedCurve, output);
+ }
+
+ return generateEphemeralClientKeyExchange(random, ecPointFormats, ecParams, output);
+ }
+
public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key) throws IOException
{
// TODO Check RFC 4492 for validation
@@ -456,10 +587,11 @@ public class TlsECCUtils
BigInteger prime_p = readECParameter(input);
BigInteger a = readECFieldElement(prime_p.bitLength(), input);
BigInteger b = readECFieldElement(prime_p.bitLength(), input);
- ECCurve curve = new ECCurve.Fp(prime_p, a, b);
- ECPoint base = deserializeECPoint(ecPointFormats, curve, TlsUtils.readOpaque8(input));
+ byte[] baseEncoding = TlsUtils.readOpaque8(input);
BigInteger order = readECParameter(input);
BigInteger cofactor = readECParameter(input);
+ ECCurve curve = new ECCurve.Fp(prime_p, a, b, order, cofactor);
+ ECPoint base = deserializeECPoint(ecPointFormats, curve, baseEncoding);
return new ECDomainParameters(curve, base, order, cofactor);
}
case ECCurveType.explicit_char2:
@@ -468,32 +600,30 @@ public class TlsECCUtils
int m = TlsUtils.readUint16(input);
short basis = TlsUtils.readUint8(input);
- ECCurve curve;
- switch (basis) {
- case ECBasisType.ec_basis_trinomial:
+ if (!ECBasisType.isValid(basis))
{
- int k = readECExponent(m, input);
- BigInteger a = readECFieldElement(m, input);
- BigInteger b = readECFieldElement(m, input);
- curve = new ECCurve.F2m(m, k, a, b);
- break;
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
- case ECBasisType.ec_basis_pentanomial:
+
+ int k1 = readECExponent(m, input), k2 = -1, k3 = -1;
+ if (basis == ECBasisType.ec_basis_pentanomial)
{
- int k1 = readECExponent(m, input);
- int k2 = readECExponent(m, input);
- int k3 = readECExponent(m, input);
- BigInteger a = readECFieldElement(m, input);
- BigInteger b = readECFieldElement(m, input);
- curve = new ECCurve.F2m(m, k1, k2, k3, a, b);
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ k2 = readECExponent(m, input);
+ k3 = readECExponent(m, input);
}
- ECPoint base = deserializeECPoint(ecPointFormats, curve, TlsUtils.readOpaque8(input));
+
+ BigInteger a = readECFieldElement(m, input);
+ BigInteger b = readECFieldElement(m, input);
+ byte[] baseEncoding = TlsUtils.readOpaque8(input);
BigInteger order = readECParameter(input);
BigInteger cofactor = readECParameter(input);
+
+ ECCurve curve = (basis == ECBasisType.ec_basis_pentanomial)
+ ? new ECCurve.F2m(m, k1, k2, k3, a, b, order, cofactor)
+ : new ECCurve.F2m(m, k1, a, b, order, cofactor);
+
+ ECPoint base = deserializeECPoint(ecPointFormats, curve, baseEncoding);
+
return new ECDomainParameters(curve, base, order, cofactor);
}
case ECCurveType.named_curve:
@@ -511,7 +641,7 @@ public class TlsECCUtils
checkNamedCurve(namedCurves, namedCurve);
- return TlsECCUtils.getParametersForNamedCurve(namedCurve);
+ return getParametersForNamedCurve(namedCurve);
}
default:
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
@@ -519,7 +649,7 @@ public class TlsECCUtils
}
catch (RuntimeException e)
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
@@ -561,35 +691,40 @@ public class TlsECCUtils
OutputStream output) throws IOException
{
ECCurve curve = ecParameters.getCurve();
- if (curve instanceof ECCurve.Fp)
+
+ if (ECAlgorithms.isFpCurve(curve))
{
TlsUtils.writeUint8(ECCurveType.explicit_prime, output);
- ECCurve.Fp fp = (ECCurve.Fp) curve;
- writeECParameter(fp.getQ(), output);
+ writeECParameter(curve.getField().getCharacteristic(), output);
}
- else if (curve instanceof ECCurve.F2m)
+ else if (ECAlgorithms.isF2mCurve(curve))
{
+ PolynomialExtensionField field = (PolynomialExtensionField)curve.getField();
+ int[] exponents = field.getMinimalPolynomial().getExponentsPresent();
+
TlsUtils.writeUint8(ECCurveType.explicit_char2, output);
- ECCurve.F2m f2m = (ECCurve.F2m) curve;
- int m = f2m.getM();
+ int m = exponents[exponents.length - 1];
TlsUtils.checkUint16(m);
TlsUtils.writeUint16(m, output);
- if (f2m.isTrinomial())
+ if (exponents.length == 3)
{
TlsUtils.writeUint8(ECBasisType.ec_basis_trinomial, output);
- writeECExponent(f2m.getK1(), output);
+ writeECExponent(exponents[1], output);
}
- else
+ else if (exponents.length == 5)
{
TlsUtils.writeUint8(ECBasisType.ec_basis_pentanomial, output);
- writeECExponent(f2m.getK1(), output);
- writeECExponent(f2m.getK2(), output);
- writeECExponent(f2m.getK3(), output);
+ writeECExponent(exponents[1], output);
+ writeECExponent(exponents[2], output);
+ writeECExponent(exponents[3], output);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Only trinomial and pentomial curves are supported");
}
-
}
else
{
@@ -605,7 +740,7 @@ public class TlsECCUtils
public static void writeECPoint(short[] ecPointFormats, ECPoint point, OutputStream output) throws IOException
{
- TlsUtils.writeOpaque8(TlsECCUtils.serializeECPoint(ecPointFormats, point), output);
+ TlsUtils.writeOpaque8(serializeECPoint(ecPointFormats, point), output);
}
public static void writeNamedECParameters(int namedCurve, OutputStream output) throws IOException
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 d346ef46..c7840801 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
@@ -14,7 +14,7 @@ import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.TeeInputStream;
/**
- * ECDHE key exchange (see RFC 4492)
+ * (D)TLS ECDHE key exchange (see RFC 4492).
*/
public class TlsECDHEKeyExchange
extends TlsECDHKeyExchange
@@ -43,95 +43,18 @@ 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
- {
- for (int i = 0; i < namedCurves.length; ++i)
- {
- int entry = namedCurves[i];
- if (TlsECCUtils.isSupportedNamedCurve(entry))
- {
- namedCurve = entry;
- break;
- }
- }
- }
-
- ECDomainParameters curve_params = null;
- if (namedCurve >= 0)
- {
- curve_params = TlsECCUtils.getParametersForNamedCurve(namedCurve);
- }
- else
- {
- /*
- * If no named curves are suitable, check if the client supports explicit curves.
- */
- if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
- {
- curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.secp256r1);
- }
- else if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
- {
- curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect283r1);
- }
- }
-
- if (curve_params == null)
- {
- /*
- * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find
- * a suitable curve.
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
-
- AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(context.getSecureRandom(), curve_params);
- this.ecAgreePrivateKey = (ECPrivateKeyParameters)kp.getPrivate();
-
DigestInputBuffer buf = new DigestInputBuffer();
- if (namedCurve < 0)
- {
- TlsECCUtils.writeExplicitECParameters(clientECPointFormats, curve_params, buf);
- }
- else
- {
- TlsECCUtils.writeNamedECParameters(namedCurve, buf);
- }
-
- ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic();
- TlsECCUtils.writeECPoint(clientECPointFormats, ecPublicKey.getQ(), buf);
+ this.ecAgreePrivateKey = TlsECCUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(), namedCurves,
+ clientECPointFormats, 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);
- }
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtils.getSignatureAndHashAlgorithm(
+ context, serverCredentials);
- d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash());
- }
- else
- {
- signatureAndHashAlgorithm = null;
- d = new CombinedHash();
- }
+ Digest d = TlsUtils.createHash(signatureAndHashAlgorithm);
SecurityParameters securityParameters = context.getSecurityParameters();
d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
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 94563520..3248e781 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
@@ -14,7 +14,7 @@ import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
/**
- * ECDH key exchange (see RFC 4492)
+ * (D)TLS ECDH key exchange (see RFC 4492).
*/
public class TlsECDHKeyExchange extends AbstractTlsKeyExchange
{
@@ -49,7 +49,6 @@ public class TlsECDHKeyExchange extends AbstractTlsKeyExchange
throw new IllegalArgumentException("unsupported key exchange algorithm");
}
- this.keyExchange = keyExchange;
this.namedCurves = namedCurves;
this.clientECPointFormats = clientECPointFormats;
this.serverECPointFormats = serverECPointFormats;
@@ -86,7 +85,7 @@ public class TlsECDHKeyExchange extends AbstractTlsKeyExchange
}
catch (RuntimeException e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
if (tlsSigner == null)
@@ -97,7 +96,7 @@ public class TlsECDHKeyExchange extends AbstractTlsKeyExchange
}
catch (ClassCastException e)
{
- throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
}
TlsUtils.validateKeyUsage(x509Cert, KeyUsage.keyAgreement);
@@ -159,7 +158,7 @@ public class TlsECDHKeyExchange extends AbstractTlsKeyExchange
{
// TODO Validate client cert has matching parameters (see 'TlsECCUtils.areOnSameCurve')?
- this.agreementCredentials = (TlsAgreementCredentials) clientCredentials;
+ this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
}
else if (clientCredentials instanceof TlsSignerCredentials)
{
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 d7f80649..aa4c546e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
@@ -4,6 +4,7 @@ import org.bouncycastle.crypto.DSA;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
public class TlsECDSASigner
extends TlsDSASigner
@@ -13,9 +14,9 @@ public class TlsECDSASigner
return publicKey instanceof ECPublicKeyParameters;
}
- protected DSA createDSAImpl()
+ protected DSA createDSAImpl(short hashAlgorithm)
{
- return new ECDSASigner();
+ return new ECDSASigner(new HMacDSAKCalculator(TlsUtils.createHash(hashAlgorithm)));
}
protected short getSignatureAlgorithm()
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 eddf6847..f2928963 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
@@ -2,8 +2,7 @@ package org.bouncycastle.crypto.tls;
import java.io.IOException;
-public interface TlsEncryptionCredentials
- extends TlsCredentials
+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
index fbc39dd9..8e50f57d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java
@@ -9,6 +9,8 @@ import org.bouncycastle.util.Integers;
public class TlsExtensionsUtils
{
+ public static final Integer EXT_encrypt_then_mac = Integers.valueOf(ExtensionType.encrypt_then_mac);
+ public static final Integer EXT_extended_master_secret = Integers.valueOf(ExtensionType.extended_master_secret);
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);
@@ -20,6 +22,16 @@ public class TlsExtensionsUtils
return extensions == null ? new Hashtable() : extensions;
}
+ public static void addEncryptThenMACExtension(Hashtable extensions)
+ {
+ extensions.put(EXT_encrypt_then_mac, createEncryptThenMACExtension());
+ }
+
+ public static void addExtendedMasterSecretExtension(Hashtable extensions)
+ {
+ extensions.put(EXT_extended_master_secret, createExtendedMasterSecretExtension());
+ }
+
public static void addHeartbeatExtension(Hashtable extensions, HeartbeatExtension heartbeatExtension)
throws IOException
{
@@ -77,6 +89,18 @@ public class TlsExtensionsUtils
return extensionData == null ? null : readStatusRequestExtension(extensionData);
}
+ public static boolean hasEncryptThenMACExtension(Hashtable extensions) throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_encrypt_then_mac);
+ return extensionData == null ? false : readEncryptThenMACExtension(extensionData);
+ }
+
+ public static boolean hasExtendedMasterSecretExtension(Hashtable extensions) throws IOException
+ {
+ byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_extended_master_secret);
+ return extensionData == null ? false : readExtendedMasterSecretExtension(extensionData);
+ }
+
public static boolean hasTruncatedHMacExtension(Hashtable extensions) throws IOException
{
byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_truncated_hmac);
@@ -88,6 +112,16 @@ public class TlsExtensionsUtils
return TlsUtils.EMPTY_BYTES;
}
+ public static byte[] createEncryptThenMACExtension()
+ {
+ return createEmptyExtensionData();
+ }
+
+ public static byte[] createExtendedMasterSecretExtension()
+ {
+ return createEmptyExtensionData();
+ }
+
public static byte[] createHeartbeatExtension(HeartbeatExtension heartbeatExtension)
throws IOException
{
@@ -106,12 +140,11 @@ public class TlsExtensionsUtils
public static byte[] createMaxFragmentLengthExtension(short maxFragmentLength)
throws IOException
{
- if (!MaxFragmentLength.isValid(maxFragmentLength))
- {
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
+ TlsUtils.checkUint8(maxFragmentLength);
- return new byte[]{ (byte)maxFragmentLength };
+ byte[] extensionData = new byte[1];
+ TlsUtils.writeUint8(maxFragmentLength, extensionData, 0);
+ return extensionData;
}
public static byte[] createServerNameExtension(ServerNameList serverNameList)
@@ -149,6 +182,31 @@ public class TlsExtensionsUtils
return createEmptyExtensionData();
}
+ private static boolean readEmptyExtensionData(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;
+ }
+
+ public static boolean readEncryptThenMACExtension(byte[] extensionData) throws IOException
+ {
+ return readEmptyExtensionData(extensionData);
+ }
+
+ public static boolean readExtendedMasterSecretExtension(byte[] extensionData) throws IOException
+ {
+ return readEmptyExtensionData(extensionData);
+ }
+
public static HeartbeatExtension readHeartbeatExtension(byte[] extensionData)
throws IOException
{
@@ -179,14 +237,7 @@ public class TlsExtensionsUtils
throw new TlsFatalAlert(AlertDescription.decode_error);
}
- short maxFragmentLength = (short)extensionData[0];
-
- if (!MaxFragmentLength.isValid(maxFragmentLength))
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
-
- return maxFragmentLength;
+ return TlsUtils.readUint8(extensionData, 0);
}
public static ServerNameList readServerNameExtension(byte[] extensionData)
@@ -223,18 +274,8 @@ public class TlsExtensionsUtils
return statusRequest;
}
- private static boolean readTruncatedHMacExtension(byte[] extensionData) throws IOException
+ public 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;
+ return readEmptyExtensionData(extensionData);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java
index 61cec318..dfbb09e8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsFatalAlert.java
@@ -7,15 +7,31 @@ public class TlsFatalAlert
{
private static final long serialVersionUID = 3584313123679111168L;
- private short alertDescription;
+ protected short alertDescription;
+
+ // TODO Some day we might be able to just pass this down to IOException (1.6+)
+ protected Throwable alertCause;
public TlsFatalAlert(short alertDescription)
{
+ this(alertDescription, null);
+ }
+
+ public TlsFatalAlert(short alertDescription, Throwable alertCause)
+ {
+ super(AlertDescription.getText(alertDescription));
+
this.alertDescription = alertDescription;
+ this.alertCause = alertCause;
}
public short getAlertDescription()
{
return alertDescription;
}
+
+ public Throwable getCause()
+ {
+ return alertCause;
+ }
}
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 1cb0f4d4..4ccfe72b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
@@ -2,7 +2,7 @@ package org.bouncycastle.crypto.tls;
import org.bouncycastle.crypto.Digest;
-interface TlsHandshakeHash
+public interface TlsHandshakeHash
extends Digest
{
void init(TlsContext context);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java
index 9509dc4f..a2cf74e8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsInputStream.java
@@ -17,6 +17,12 @@ class TlsInputStream
this.handler = handler;
}
+ public int available()
+ throws IOException
+ {
+ return this.handler.applicationDataAvailable();
+ }
+
public int read(byte[] buf, int offset, int len)
throws IOException
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java
index 91590cec..83cd0b7b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsKeyExchange.java
@@ -5,11 +5,10 @@ import java.io.InputStream;
import java.io.OutputStream;
/**
- * A generic interface for key exchange implementations in TLS 1.0/1.1.
+ * A generic interface for key exchange implementations in (D)TLS.
*/
public interface TlsKeyExchange
{
-
void init(TlsContext context);
void skipServerCredentials()
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 20dfef89..00e0c79f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
@@ -26,7 +26,7 @@ public class TlsMac
* @param digest The digest to use.
* @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.
+ * @param keyLen The length of the key.
*/
public TlsMac(TlsContext context, Digest digest, byte[] key, int keyOff, int keyLen)
{
@@ -105,15 +105,6 @@ 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();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKIdentityManager.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKIdentityManager.java
new file mode 100644
index 00000000..c0ecf32d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKIdentityManager.java
@@ -0,0 +1,8 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsPSKIdentityManager
+{
+ byte[] getHint();
+
+ byte[] getPSK(byte[] identity);
+}
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 7217bac6..43c651d2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
@@ -4,6 +4,7 @@ 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;
@@ -12,32 +13,44 @@ import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
/**
- * TLS 1.0 PSK key exchange (RFC 4279).
+ * (D)TLS PSK key exchange (RFC 4279).
*/
public class TlsPSKKeyExchange
extends AbstractTlsKeyExchange
{
protected TlsPSKIdentity pskIdentity;
+ protected TlsPSKIdentityManager pskIdentityManager;
+
protected DHParameters dhParameters;
protected int[] namedCurves;
protected short[] clientECPointFormats, serverECPointFormats;
protected byte[] psk_identity_hint = null;
+ protected byte[] psk = null;
protected DHPrivateKeyParameters dhAgreePrivateKey = null;
protected DHPublicKeyParameters dhAgreePublicKey = null;
+ protected ECPrivateKeyParameters ecAgreePrivateKey = null;
+ protected ECPublicKeyParameters ecAgreePublicKey = null;
+
protected AsymmetricKeyParameter serverPublicKey = null;
protected RSAKeyParameters rsaServerPublicKey = null;
protected TlsEncryptionCredentials serverCredentials = null;
protected byte[] premasterSecret;
public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity,
- DHParameters dhParameters, int[] namedCurves, short[] clientECPointFormats, short[] serverECPointFormats)
+ TlsPSKIdentityManager pskIdentityManager, DHParameters dhParameters, int[] namedCurves,
+ short[] clientECPointFormats, short[] serverECPointFormats)
{
super(keyExchange, supportedSignatureAlgorithms);
@@ -53,14 +66,14 @@ public class TlsPSKKeyExchange
}
this.pskIdentity = pskIdentity;
+ this.pskIdentityManager = pskIdentityManager;
this.dhParameters = dhParameters;
this.namedCurves = namedCurves;
this.clientECPointFormats = clientECPointFormats;
this.serverECPointFormats = serverECPointFormats;
}
- public void skipServerCredentials()
- throws IOException
+ public void skipServerCredentials() throws IOException
{
if (keyExchange == KeyExchangeAlgorithm.RSA_PSK)
{
@@ -68,8 +81,7 @@ public class TlsPSKKeyExchange
}
}
- public void processServerCredentials(TlsCredentials serverCredentials)
- throws IOException
+ public void processServerCredentials(TlsCredentials serverCredentials) throws IOException
{
if (!(serverCredentials instanceof TlsEncryptionCredentials))
{
@@ -83,8 +95,7 @@ public class TlsPSKKeyExchange
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;
+ this.psk_identity_hint = pskIdentityManager.getHint();
if (this.psk_identity_hint == null && !requiresServerKeyExchange())
{
@@ -114,14 +125,14 @@ public class TlsPSKKeyExchange
}
else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
{
- // TODO[RFC 5489]
+ this.ecAgreePrivateKey = TlsECCUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(),
+ namedCurves, clientECPointFormats, buf);
}
return buf.toByteArray();
}
- public void processServerCertificate(Certificate serverCertificate)
- throws IOException
+ public void processServerCertificate(Certificate serverCertificate) throws IOException
{
if (keyExchange != KeyExchangeAlgorithm.RSA_PSK)
{
@@ -141,7 +152,7 @@ public class TlsPSKKeyExchange
}
catch (RuntimeException e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
// Sanity check the PublicKeyFactory
@@ -169,8 +180,7 @@ public class TlsPSKKeyExchange
}
}
- public void processServerKeyExchange(InputStream input)
- throws IOException
+ public void processServerKeyExchange(InputStream input) throws IOException
{
this.psk_identity_hint = TlsUtils.readOpaque16(input);
@@ -179,27 +189,30 @@ public class TlsPSKKeyExchange
ServerDHParams serverDHParams = ServerDHParams.parse(input);
this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(serverDHParams.getPublicKey());
+ this.dhParameters = dhAgreePublicKey.getParameters();
}
else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
{
- // TODO[RFC 5489]
+ ECDomainParameters ecParams = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, input);
+
+ byte[] point = TlsUtils.readOpaque8(input);
+
+ this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
+ clientECPointFormats, ecParams, point));
}
}
- 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
{
if (psk_identity_hint == null)
{
@@ -211,18 +224,30 @@ public class TlsPSKKeyExchange
}
byte[] psk_identity = pskIdentity.getPSKIdentity();
+ if (psk_identity == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ this.psk = pskIdentity.getPSK();
+ if (psk == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
TlsUtils.writeOpaque16(psk_identity, output);
+ context.getSecurityParameters().pskIdentity = Arrays.clone(psk_identity);
+
if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
{
this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
- dhAgreePublicKey.getParameters(), output);
+ dhParameters, output);
}
else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
{
- // TODO[RFC 5489]
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ this.ecAgreePrivateKey = TlsECCUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
+ serverECPointFormats, ecAgreePublicKey.getParameters(), output);
}
else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
{
@@ -231,15 +256,61 @@ public class TlsPSKKeyExchange
}
}
- public byte[] generatePremasterSecret()
- throws IOException
+ public void processClientKeyExchange(InputStream input) throws IOException
+ {
+ byte[] psk_identity = TlsUtils.readOpaque16(input);
+
+ this.psk = pskIdentityManager.getPSK(psk_identity);
+ if (psk == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.unknown_psk_identity);
+ }
+
+ context.getSecurityParameters().pskIdentity = psk_identity;
+
+ if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+ {
+ BigInteger Yc = TlsDHUtils.readDHParameter(input);
+
+ this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Yc, dhParameters));
+ }
+ else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+ {
+ byte[] point = TlsUtils.readOpaque8(input);
+
+ ECDomainParameters curve_params = this.ecAgreePrivateKey.getParameters();
+
+ this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
+ serverECPointFormats, curve_params, point));
+ }
+ else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+ {
+ byte[] encryptedPreMasterSecret;
+ if (TlsUtils.isSSL(context))
+ {
+ // TODO Do any SSLv3 clients actually include the length?
+ encryptedPreMasterSecret = Streams.readAll(input);
+ }
+ else
+ {
+ encryptedPreMasterSecret = TlsUtils.readOpaque16(input);
+ }
+
+ this.premasterSecret = serverCredentials.decryptPreMasterSecret(encryptedPreMasterSecret);
+ }
+ }
+
+ public byte[] generatePremasterSecret() throws IOException
{
- byte[] psk = pskIdentity.getPSK();
byte[] other_secret = generateOtherSecret(psk.length);
ByteArrayOutputStream buf = new ByteArrayOutputStream(4 + other_secret.length + psk.length);
TlsUtils.writeOpaque16(other_secret, buf);
TlsUtils.writeOpaque16(psk, buf);
+
+ Arrays.fill(psk, (byte)0);
+ this.psk = null;
+
return buf.toByteArray();
}
@@ -257,7 +328,11 @@ public class TlsPSKKeyExchange
if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
{
- // TODO[RFC 5489]
+ if (ecAgreePrivateKey != null)
+ {
+ return TlsECCUtils.calculateECDHBasicAgreement(ecAgreePublicKey, ecAgreePrivateKey);
+ }
+
throw new TlsFatalAlert(AlertDescription.internal_error);
}
@@ -269,8 +344,7 @@ public class TlsPSKKeyExchange
return new byte[pskLength];
}
- protected RSAKeyParameters validateRSAPublicKey(RSAKeyParameters key)
- throws IOException
+ protected RSAKeyParameters validateRSAPublicKey(RSAKeyParameters key) throws IOException
{
// TODO What is the minimum bit length required?
// key.getModulus().bitLength();
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 88780eaf..aa0dcf86 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
@@ -4,6 +4,17 @@ import java.io.IOException;
public interface TlsPeer
{
+ /**
+ * draft-mathewson-no-gmtunixtime-00 2. "If existing users of a TLS implementation may rely on
+ * gmt_unix_time containing the current time, we recommend that implementors MAY provide the
+ * ability to set gmt_unix_time as an option only, off by default."
+ *
+ * @return <code>true</code> if the current time should be used in the gmt_unix_time field of
+ * Random, or <code>false</code> if gmt_unix_time should contain a cryptographically
+ * random value.
+ */
+ boolean shouldUseGMTUnixTime();
+
void notifySecureRenegotiation(boolean secureNegotiation) throws IOException;
TlsCompression getCompression() throws IOException;
@@ -16,9 +27,9 @@ public interface TlsPeer
* @param alertLevel {@link AlertLevel}
* @param alertDescription {@link AlertDescription}
* @param message A human-readable message explaining what caused this alert. May be null.
- * @param cause The exception that caused this alert to be raised. May be null.
+ * @param cause The {@link Throwable} that caused this alert to be raised. May be null.
*/
- void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause);
+ void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause);
/**
* This method will be called when an alert is received from the remote peer.
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 2c3b0941..c116401f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
@@ -12,12 +12,10 @@ import java.util.Hashtable;
import java.util.Vector;
import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.prng.RandomGenerator;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Integers;
-/**
- * An implementation of all high level protocols in TLS 1.0/1.1.
- */
public abstract class TlsProtocol
{
protected static final Integer EXT_RenegotiationInfo = Integers.valueOf(ExtensionType.renegotiation_info);
@@ -52,11 +50,12 @@ public abstract class TlsProtocol
private ByteQueue applicationDataQueue = new ByteQueue();
private ByteQueue alertQueue = new ByteQueue(2);
private ByteQueue handshakeQueue = new ByteQueue();
+// private ByteQueue heartbeatQueue = new ByteQueue();
/*
* The Record Stream we use
*/
- protected RecordStream recordStream;
+ RecordStream recordStream;
protected SecureRandom secureRandom;
private TlsInputStream tlsInputStream = null;
@@ -91,7 +90,9 @@ public abstract class TlsProtocol
this.secureRandom = secureRandom;
}
- protected abstract AbstractTlsContext getContext();
+ protected abstract TlsContext getContext();
+
+ abstract AbstractTlsContext getContextAdmin();
protected abstract TlsPeer getPeer();
@@ -172,6 +173,8 @@ public abstract class TlsProtocol
.setCompressionAlgorithm(this.securityParameters.compressionAlgorithm)
.setMasterSecret(this.securityParameters.masterSecret)
.setPeerCertificate(this.peerCertificate)
+ .setPSKIdentity(this.securityParameters.pskIdentity)
+ .setSRPIdentity(this.securityParameters.srpIdentity)
// TODO Consider filtering extensions that aren't relevant to resumed sessions
.setServerExtensions(this.serverExtensions)
.build();
@@ -179,7 +182,7 @@ public abstract class TlsProtocol
this.tlsSession = new TlsSessionImpl(this.tlsSession.getSessionID(), this.sessionParameters);
}
- getContext().setResumableSession(this.tlsSession);
+ getContextAdmin().setResumableSession(this.tlsSession);
}
getPeer().notifyHandshakeComplete();
@@ -227,7 +230,14 @@ public abstract class TlsProtocol
}
case ContentType.heartbeat:
{
+ if (!appDataReady)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
// TODO[RFC 6520]
+// heartbeatQueue.addData(buf, offset, len);
+// processHeartbeat();
+ break;
}
default:
/*
@@ -235,6 +245,7 @@ public abstract class TlsProtocol
*
* RFC2246 defines on page 13, that we should ignore this.
*/
+ break;
}
}
@@ -248,18 +259,17 @@ public abstract class TlsProtocol
/*
* We need the first 4 bytes, they contain type and length of the message.
*/
- if (handshakeQueue.size() >= 4)
+ if (handshakeQueue.available() >= 4)
{
byte[] beginning = new byte[4];
handshakeQueue.read(beginning, 0, 4, 0);
- ByteArrayInputStream bis = new ByteArrayInputStream(beginning);
- short type = TlsUtils.readUint8(bis);
- int len = TlsUtils.readUint24(bis);
+ short type = TlsUtils.readUint8(beginning, 0);
+ int len = TlsUtils.readUint24(beginning, 1);
/*
* Check if we have enough bytes in the buffer to read the full message.
*/
- if (handshakeQueue.size() >= (len + 4))
+ if (handshakeQueue.available() >= (len + 4))
{
/*
* Read the message.
@@ -313,7 +323,7 @@ public abstract class TlsProtocol
private void processAlert()
throws IOException
{
- while (alertQueue.size() >= 2)
+ while (alertQueue.available() >= 2)
{
/*
* An alert is always 2 bytes. Read the alert.
@@ -378,19 +388,27 @@ public abstract class TlsProtocol
throw new TlsFatalAlert(AlertDescription.decode_error);
}
- if (this.receivedChangeCipherSpec)
+ if (this.receivedChangeCipherSpec
+ || alertQueue.available() > 0
+ || handshakeQueue.available() > 0)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
+ recordStream.receivedReadCipherSpec();
+
this.receivedChangeCipherSpec = true;
- recordStream.receivedReadCipherSpec();
-
handleChangeCipherSpecMessage();
}
}
+ protected int applicationDataAvailable()
+ throws IOException
+ {
+ return applicationDataQueue.available();
+ }
+
/**
* Read data from the network. The method will return immediately, if there is still some data
* left in the buffer, or block until some application data has been read from the network.
@@ -409,7 +427,7 @@ public abstract class TlsProtocol
return 0;
}
- while (applicationDataQueue.size() == 0)
+ while (applicationDataQueue.available() == 0)
{
/*
* We need to read some data.
@@ -433,7 +451,7 @@ public abstract class TlsProtocol
safeReadRecord();
}
- len = Math.min(len, applicationDataQueue.size());
+ len = Math.min(len, applicationDataQueue.available());
applicationDataQueue.removeData(buf, offset, len, 0);
return len;
}
@@ -452,7 +470,7 @@ public abstract class TlsProtocol
}
catch (TlsFatalAlert e)
{
- if (!this.closed)
+ if (!closed)
{
this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to read record", e);
}
@@ -460,7 +478,7 @@ public abstract class TlsProtocol
}
catch (IOException e)
{
- if (!this.closed)
+ if (!closed)
{
this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
}
@@ -468,7 +486,7 @@ public abstract class TlsProtocol
}
catch (RuntimeException e)
{
- if (!this.closed)
+ if (!closed)
{
this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
}
@@ -485,7 +503,7 @@ public abstract class TlsProtocol
}
catch (TlsFatalAlert e)
{
- if (!this.closed)
+ if (!closed)
{
this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to write record", e);
}
@@ -511,9 +529,9 @@ public abstract class TlsProtocol
/**
* Send some application data to the remote system.
- * <p/>
+ * <p>
* The method will handle fragmentation internally.
- *
+ * </p>
* @param buf The buffer with the data.
* @param offset The position in the buffer where the data is placed.
* @param len The length of the data.
@@ -602,7 +620,7 @@ public abstract class TlsProtocol
* @throws IOException
* If alert was fatal.
*/
- protected void failWithError(short alertLevel, short alertDescription, String message, Exception cause)
+ protected void failWithError(short alertLevel, short alertDescription, String message, Throwable cause)
throws IOException
{
/*
@@ -671,7 +689,7 @@ public abstract class TlsProtocol
}
}
- protected void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause)
+ protected void raiseAlert(short alertLevel, short alertDescription, String message, Throwable cause)
throws IOException
{
getPeer().notifyAlertRaised(alertLevel, alertDescription, message, cause);
@@ -697,7 +715,7 @@ public abstract class TlsProtocol
certificate = Certificate.EMPTY_CHAIN;
}
- if (certificate.getLength() == 0)
+ if (certificate.isEmpty())
{
TlsContext context = getContext();
if (!context.isServer())
@@ -705,8 +723,8 @@ public abstract class TlsProtocol
ProtocolVersion serverVersion = getContext().getServerVersion();
if (serverVersion.isSSL())
{
- String message = serverVersion.toString() + " client didn't provide credentials";
- raiseWarning(AlertDescription.no_certificate, message);
+ String errorMessage = serverVersion.toString() + " client didn't provide credentials";
+ raiseWarning(AlertDescription.no_certificate, errorMessage);
return;
}
}
@@ -752,15 +770,10 @@ public abstract class TlsProtocol
protected byte[] createVerifyData(boolean isServer)
{
TlsContext context = getContext();
-
- if (isServer)
- {
- return TlsUtils.calculateVerifyData(context, ExporterLabel.server_finished,
- getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_SERVER));
- }
-
- return TlsUtils.calculateVerifyData(context, ExporterLabel.client_finished,
- getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_CLIENT));
+ String asciiLabel = isServer ? ExporterLabel.server_finished : ExporterLabel.client_finished;
+ byte[] sslSender = isServer ? TlsUtils.SSL_SERVER : TlsUtils.SSL_CLIENT;
+ byte[] hash = getCurrentPRFHash(context, recordStream.getHandshakeHash(), sslSender);
+ return TlsUtils.calculateVerifyData(context, asciiLabel, hash);
}
/**
@@ -793,7 +806,13 @@ public abstract class TlsProtocol
recordStream.flush();
}
- protected short processMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions, short alertDescription)
+ protected boolean isClosed()
+ {
+ return closed;
+ }
+
+ protected short processMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions,
+ short alertDescription)
throws IOException
{
short maxFragmentLength = TlsExtensionsUtils.getMaxFragmentLengthExtension(serverExtensions);
@@ -822,18 +841,16 @@ public abstract class TlsProtocol
}
}
- protected static byte[] createRandomBlock(SecureRandom random)
+ protected static byte[] createRandomBlock(boolean useGMTUnixTime, RandomGenerator randomGenerator)
{
- random.setSeed(System.currentTimeMillis());
-
byte[] result = new byte[32];
- random.nextBytes(result);
- /*
- * 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);
+ randomGenerator.nextBytes(result);
+
+ if (useGMTUnixTime)
+ {
+ TlsUtils.writeGMTUnixTime(result, 0);
+ }
+
return result;
}
@@ -987,18 +1004,31 @@ public abstract class TlsProtocol
switch (ciphersuite)
{
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256:
case CipherSuite.TLS_DH_DSS_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_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_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_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_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_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_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_PSK_WITH_CAMELLIA_128_GCM_SHA256:
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:
@@ -1006,14 +1036,32 @@ public abstract class TlsProtocol
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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_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_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_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:
@@ -1021,7 +1069,9 @@ public abstract class TlsProtocol
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_PSK_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_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:
@@ -1029,6 +1079,9 @@ public abstract class TlsProtocol
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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
{
if (isTLSv12)
@@ -1038,22 +1091,39 @@ public abstract class TlsProtocol
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_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_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_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_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_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_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_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_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
{
if (isTLSv12)
{
@@ -1063,12 +1133,16 @@ public abstract class TlsProtocol
}
case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_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_CAMELLIA_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_CAMELLIA_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_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
{
if (isTLSv12)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocolHandler.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocolHandler.java
deleted file mode 100644
index e4fcf285..00000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocolHandler.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.bouncycastle.crypto.tls;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.SecureRandom;
-
-/**
- * @deprecated use TlsClientProtocol instead
- */
-public class TlsProtocolHandler
- extends TlsClientProtocol
-{
-
- public TlsProtocolHandler(InputStream is, OutputStream os)
- {
- super(is, os);
- }
-
- public TlsProtocolHandler(InputStream is, OutputStream os, SecureRandom sr)
- {
- super(is, os, sr);
- }
-}
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 89709685..3efdd6c8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
@@ -13,7 +13,7 @@ import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.util.io.Streams;
/**
- * TLS 1.0/1.1 and SSLv3 RSA key exchange.
+ * (D)TLS and SSLv3 RSA key exchange.
*/
public class TlsRSAKeyExchange
extends AbstractTlsKeyExchange
@@ -67,7 +67,7 @@ public class TlsRSAKeyExchange
}
catch (RuntimeException e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
// Sanity check the PublicKeyFactory
@@ -130,7 +130,7 @@ public class TlsRSAKeyExchange
encryptedPreMasterSecret = TlsUtils.readOpaque16(input);
}
- this.premasterSecret = TlsRSAUtils.safeDecryptPreMasterSecret(context, serverCredentials, encryptedPreMasterSecret);
+ this.premasterSecret = serverCredentials.decryptPreMasterSecret(encryptedPreMasterSecret);
}
public byte[] generatePremasterSecret()
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 e3856bdc..99f1fb32 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
@@ -8,6 +8,7 @@ import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.util.Arrays;
public class TlsRSAUtils
{
@@ -43,39 +44,45 @@ public class TlsRSAUtils
/*
* This should never happen, only during decryption.
*/
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
return premasterSecret;
}
- public static byte[] safeDecryptPreMasterSecret(TlsContext context, TlsEncryptionCredentials encryptionCredentials,
+ public static byte[] safeDecryptPreMasterSecret(TlsContext context, RSAKeyParameters rsaServerPrivateKey,
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.
+ * Generate 48 random bytes we can use as a Pre-Master-Secret, if the
+ * PKCS1 padding check should fail.
*/
- byte[] R = new byte[48];
- context.getSecureRandom().nextBytes(R);
+ byte[] fallback = new byte[48];
+ context.getSecureRandom().nextBytes(fallback);
- byte[] M = TlsUtils.EMPTY_BYTES;
+ byte[] M = Arrays.clone(fallback);
try
{
- M = encryptionCredentials.decryptPreMasterSecret(encryptedPreMasterSecret);
+ PKCS1Encoding encoding = new PKCS1Encoding(new RSABlindedEngine(), fallback);
+ encoding.init(false,
+ new ParametersWithRandom(rsaServerPrivateKey, context.getSecureRandom()));
+
+ M = encoding.processBlock(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.length);
}
catch (Exception e)
{
/*
+ * This should never happen since the decryption should never throw an exception
+ * and return a random value instead.
+ *
* 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
@@ -83,12 +90,6 @@ public class TlsRSAUtils
*/
}
- 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 [..].
@@ -96,21 +97,35 @@ public class TlsRSAUtils
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.
+ * 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.
+ *
+ * So there is nothing to do here.
*/
}
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.
+ * OK, we need to compare the version number in the decrypted Pre-Master-Secret with the
+ * clientVersion received during the handshake. If they don't match, we replace the
+ * decrypted Pre-Master-Secret with a random one.
*/
- TlsUtils.writeVersion(clientVersion, M, 0);
- }
+ int correct = (clientVersion.getMajorVersion() ^ (M[0] & 0xff))
+ | (clientVersion.getMinorVersion() ^ (M[1] & 0xff));
+ correct |= correct >> 1;
+ correct |= correct >> 2;
+ correct |= correct >> 4;
+ int mask = ~((correct & 1) - 1);
+ /*
+ * mask will be all bits set to 0xff if the version number differed.
+ */
+ for (int i = 0; i < 48; i++)
+ {
+ M[i] = (byte)((M[i] & (~mask)) | (fallback[i] & mask));
+ }
+ }
return M;
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRuntimeException.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRuntimeException.java
deleted file mode 100644
index 3340e490..00000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRuntimeException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.bouncycastle.crypto.tls;
-
-public class TlsRuntimeException
- extends RuntimeException
-{
- private static final long serialVersionUID = 1928023487348344086L;
-
- Throwable e;
-
- public TlsRuntimeException(String message, Throwable e)
- {
- super(message);
-
- this.e = e;
- }
-
- public TlsRuntimeException(String message)
- {
- super(message);
- }
-
- public Throwable getCause()
- {
- return e;
- }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPGroupVerifier.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPGroupVerifier.java
new file mode 100644
index 00000000..81767d11
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPGroupVerifier.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
+
+public interface TlsSRPGroupVerifier
+{
+ /**
+ * Check whether the given SRP group parameters are acceptable for use.
+ *
+ * @param group the {@link SRP6GroupParameters} to check
+ * @return true if (and only if) the specified group parameters are acceptable
+ */
+ boolean accept(SRP6GroupParameters group);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPIdentityManager.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPIdentityManager.java
new file mode 100644
index 00000000..fbf50b1d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPIdentityManager.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsSRPIdentityManager
+{
+ /**
+ * Lookup the {@link TlsSRPLoginParameters} corresponding to the specified identity.
+ *
+ * NOTE: To avoid "identity probing", unknown identities SHOULD be handled as recommended in RFC
+ * 5054 2.5.1.3. {@link SimulatedTlsSRPIdentityManager} is provided for this purpose.
+ *
+ * @param identity
+ * the SRP identity sent by the connecting client
+ * @return the {@link TlsSRPLoginParameters} for the specified identity, or else 'simulated'
+ * parameters if the identity is not recognized. A null value is also allowed, but not
+ * recommended.
+ */
+ TlsSRPLoginParameters getLoginParameters(byte[] identity);
+}
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 452fbf90..1ec6ee2e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
@@ -9,59 +9,93 @@ import java.util.Vector;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.agreement.srp.SRP6Client;
+import org.bouncycastle.crypto.agreement.srp.SRP6Server;
import org.bouncycastle.crypto.agreement.srp.SRP6Util;
-import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.io.TeeInputStream;
/**
- * TLS 1.1 SRP key exchange (RFC 5054).
+ * (D)TLS SRP key exchange (RFC 5054).
*/
public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
{
+ protected static TlsSigner createSigner(int keyExchange)
+ {
+ switch (keyExchange)
+ {
+ case KeyExchangeAlgorithm.SRP:
+ return null;
+ case KeyExchangeAlgorithm.SRP_RSA:
+ return new TlsRSASigner();
+ case KeyExchangeAlgorithm.SRP_DSS:
+ return new TlsDSSSigner();
+ default:
+ throw new IllegalArgumentException("unsupported key exchange algorithm");
+ }
+ }
+
protected TlsSigner tlsSigner;
+ protected TlsSRPGroupVerifier groupVerifier;
protected byte[] identity;
protected byte[] password;
protected AsymmetricKeyParameter serverPublicKey = null;
- protected byte[] s = null;
- protected BigInteger B = null;
- protected SRP6Client srpClient = new SRP6Client();
+ protected SRP6GroupParameters srpGroup = null;
+ protected SRP6Client srpClient = null;
+ protected SRP6Server srpServer = null;
+ protected BigInteger srpPeerCredentials = null;
+ protected BigInteger srpVerifier = null;
+ protected byte[] srpSalt = null;
+
+ protected TlsSignerCredentials serverCredentials = null;
+ /**
+ * @deprecated Use constructor taking an explicit 'groupVerifier' argument
+ */
public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity, byte[] password)
{
- super(keyExchange, supportedSignatureAlgorithms);
+ this(keyExchange, supportedSignatureAlgorithms, new DefaultTlsSRPGroupVerifier(), identity, password);
+ }
- switch (keyExchange)
- {
- case KeyExchangeAlgorithm.SRP:
- this.tlsSigner = null;
- break;
- case KeyExchangeAlgorithm.SRP_RSA:
- this.tlsSigner = new TlsRSASigner();
- break;
- case KeyExchangeAlgorithm.SRP_DSS:
- this.tlsSigner = new TlsDSSSigner();
- break;
- default:
- throw new IllegalArgumentException("unsupported key exchange algorithm");
- }
+ public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsSRPGroupVerifier groupVerifier,
+ byte[] identity, byte[] password)
+ {
+ super(keyExchange, supportedSignatureAlgorithms);
- this.keyExchange = keyExchange;
+ this.tlsSigner = createSigner(keyExchange);
+ this.groupVerifier = groupVerifier;
this.identity = identity;
this.password = password;
+ this.srpClient = new SRP6Client();
+ }
+
+ public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity,
+ TlsSRPLoginParameters loginParameters)
+ {
+ super(keyExchange, supportedSignatureAlgorithms);
+
+ this.tlsSigner = createSigner(keyExchange);
+ this.identity = identity;
+ this.srpServer = new SRP6Server();
+ this.srpGroup = loginParameters.getGroup();
+ this.srpVerifier = loginParameters.getVerifier();
+ this.srpSalt = loginParameters.getSalt();
}
public void init(TlsContext context)
{
super.init(context);
- if (this.tlsSigner != null) {
+ if (this.tlsSigner != null)
+ {
this.tlsSigner.init(context);
}
}
@@ -94,7 +128,7 @@ public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
}
catch (RuntimeException e)
{
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
if (!tlsSigner.isValidPublicKey(this.serverPublicKey))
@@ -107,11 +141,62 @@ public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
super.processServerCertificate(serverCertificate);
}
+ public void processServerCredentials(TlsCredentials serverCredentials)
+ throws IOException
+ {
+ if ((keyExchange == KeyExchangeAlgorithm.SRP) || !(serverCredentials instanceof TlsSignerCredentials))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ processServerCertificate(serverCredentials.getCertificate());
+
+ this.serverCredentials = (TlsSignerCredentials)serverCredentials;
+ }
+
public boolean requiresServerKeyExchange()
{
return true;
}
+ public byte[] generateServerKeyExchange() throws IOException
+ {
+ srpServer.init(srpGroup, srpVerifier, TlsUtils.createHash(HashAlgorithm.sha1), context.getSecureRandom());
+ BigInteger B = srpServer.generateServerCredentials();
+
+ ServerSRPParams srpParams = new ServerSRPParams(srpGroup.getN(), srpGroup.getG(), srpSalt, B);
+
+ DigestInputBuffer buf = new DigestInputBuffer();
+
+ srpParams.encode(buf);
+
+ if (serverCredentials != null)
+ {
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtils.getSignatureAndHashAlgorithm(
+ context, serverCredentials);
+
+ Digest d = TlsUtils.createHash(signatureAndHashAlgorithm);
+
+ SecurityParameters securityParameters = context.getSecurityParameters();
+ d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
+ d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
+ buf.updateDigest(d);
+
+ byte[] hash = new byte[d.getDigestSize()];
+ d.doFinal(hash, 0);
+
+ byte[] signature = serverCredentials.generateCertificateSignature(hash);
+
+ DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ signed_params.encode(buf);
+ }
+
+ return buf.toByteArray();
+ }
+
public void processServerKeyExchange(InputStream input) throws IOException
{
SecurityParameters securityParameters = context.getSecurityParameters();
@@ -125,10 +210,7 @@ public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
teeIn = new TeeInputStream(input, buf);
}
- byte[] NBytes = TlsUtils.readOpaque16(teeIn);
- byte[] gBytes = TlsUtils.readOpaque16(teeIn);
- byte[] sBytes = TlsUtils.readOpaque8(teeIn);
- byte[] BBytes = TlsUtils.readOpaque16(teeIn);
+ ServerSRPParams srpParams = ServerSRPParams.parse(teeIn);
if (buf != null)
{
@@ -142,13 +224,14 @@ public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
}
}
- BigInteger N = new BigInteger(1, NBytes);
- BigInteger g = new BigInteger(1, gBytes);
+ this.srpGroup = new SRP6GroupParameters(srpParams.getN(), srpParams.getG());
- // TODO Validate group parameters (see RFC 5054)
-// throw new TlsFatalAlert(AlertDescription.insufficient_security);
+ if (!groupVerifier.accept(srpGroup))
+ {
+ throw new TlsFatalAlert(AlertDescription.insufficient_security);
+ }
- this.s = sBytes;
+ this.srpSalt = srpParams.getS();
/*
* RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if
@@ -156,14 +239,14 @@ public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
*/
try
{
- this.B = SRP6Util.validatePublicValue(N, new BigInteger(1, BBytes));
+ this.srpPeerCredentials = SRP6Util.validatePublicValue(srpGroup.getN(), srpParams.getB());
}
catch (CryptoException e)
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
- this.srpClient.init(N, g, new SHA1Digest(), context.getSecureRandom());
+ this.srpClient.init(srpGroup, TlsUtils.createHash(HashAlgorithm.sha1), context.getSecureRandom());
}
public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException
@@ -178,20 +261,44 @@ public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
public void generateClientKeyExchange(OutputStream output) throws IOException
{
- BigInteger A = srpClient.generateClientCredentials(s, this.identity, this.password);
- TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(A), output);
+ BigInteger A = srpClient.generateClientCredentials(srpSalt, identity, password);
+ TlsSRPUtils.writeSRPParameter(A, output);
+
+ context.getSecurityParameters().srpIdentity = Arrays.clone(identity);
+ }
+
+ public void processClientKeyExchange(InputStream input) throws IOException
+ {
+ /*
+ * RFC 5054 2.5.4: The server MUST abort the handshake with an "illegal_parameter" alert if
+ * A % N = 0.
+ */
+ try
+ {
+ this.srpPeerCredentials = SRP6Util.validatePublicValue(srpGroup.getN(), TlsSRPUtils.readSRPParameter(input));
+ }
+ catch (CryptoException e)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
+ }
+
+ context.getSecurityParameters().srpIdentity = Arrays.clone(identity);
}
public byte[] generatePremasterSecret() throws IOException
{
try
{
+ BigInteger S = srpServer != null
+ ? srpServer.calculateSecret(srpPeerCredentials)
+ : srpClient.calculateSecret(srpPeerCredentials);
+
// TODO Check if this needs to be a fixed size
- return BigIntegers.asUnsignedByteArray(srpClient.calculateSecret(B));
+ return BigIntegers.asUnsignedByteArray(S);
}
catch (CryptoException e)
{
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPLoginParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPLoginParameters.java
new file mode 100644
index 00000000..45ef192b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPLoginParameters.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.crypto.tls;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
+
+public class TlsSRPLoginParameters
+{
+ protected SRP6GroupParameters group;
+ protected BigInteger verifier;
+ protected byte[] salt;
+
+ public TlsSRPLoginParameters(SRP6GroupParameters group, BigInteger verifier, byte[] salt)
+ {
+ this.group = group;
+ this.verifier = verifier;
+ this.salt = salt;
+ }
+
+ public SRP6GroupParameters getGroup()
+ {
+ return group;
+ }
+
+ public byte[] getSalt()
+ {
+ return salt;
+ }
+
+ public BigInteger getVerifier()
+ {
+ return verifier;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java
index 7fe5fb8d..941d13b2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java
@@ -1,10 +1,13 @@
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.math.BigInteger;
import java.util.Hashtable;
+import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Integers;
public class TlsSRPUtils
@@ -46,4 +49,34 @@ public class TlsSRPUtils
return identity;
}
+
+ public static BigInteger readSRPParameter(InputStream input) throws IOException
+ {
+ return new BigInteger(1, TlsUtils.readOpaque16(input));
+ }
+
+ public static void writeSRPParameter(BigInteger x, OutputStream output) throws IOException
+ {
+ TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(x), output);
+ }
+
+ public static boolean isSRPCipherSuite(int cipherSuite)
+ {
+ switch (cipherSuite)
+ {
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ return true;
+
+ default:
+ return false;
+ }
+ }
}
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 85c0a9a8..2a9ae553 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
@@ -11,6 +11,8 @@ public interface TlsServer
void notifyClientVersion(ProtocolVersion clientVersion) throws IOException;
+ void notifyFallback(boolean isFallback) throws IOException;
+
void notifyOfferedCipherSuites(int[] offeredCipherSuites)
throws IOException;
@@ -78,7 +80,7 @@ public interface TlsServer
/**
* RFC 5077 3.3. NewSessionTicket Handshake Message.
- * <p/>
+ * <p>
* This method will be called (only) if a NewSessionTicket extension was sent by the server. See
* <i>RFC 5077 4. Recommended Ticket Construction</i> for recommended format and protection.
*
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 056d22aa..509c4b24 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
@@ -16,7 +16,7 @@ public class TlsServerProtocol
extends TlsProtocol
{
protected TlsServer tlsServer = null;
- protected TlsServerContextImpl tlsServerContext = null;
+ TlsServerContextImpl tlsServerContext = null;
protected TlsKeyExchange keyExchange = null;
protected TlsCredentials serverCredentials = null;
@@ -52,9 +52,12 @@ public class TlsServerProtocol
this.securityParameters = new SecurityParameters();
this.securityParameters.entity = ConnectionEnd.server;
- this.securityParameters.serverRandom = createRandomBlock(secureRandom);
this.tlsServerContext = new TlsServerContextImpl(secureRandom, securityParameters);
+
+ this.securityParameters.serverRandom = createRandomBlock(tlsServer.shouldUseGMTUnixTime(),
+ tlsServerContext.getNonceRandomGenerator());
+
this.tlsServer.init(tlsServerContext);
this.recordStream.init(tlsServerContext);
@@ -73,7 +76,12 @@ public class TlsServerProtocol
this.prepareFinishHash = null;
}
- protected AbstractTlsContext getContext()
+ protected TlsContext getContext()
+ {
+ return tlsServerContext;
+ }
+
+ AbstractTlsContext getContextAdmin()
{
return tlsServerContext;
}
@@ -363,6 +371,7 @@ public class TlsServerProtocol
default:
{
super.handleWarningMessage(description);
+ break;
}
}
}
@@ -375,7 +384,7 @@ public class TlsServerProtocol
throw new IllegalStateException();
}
- if (this.peerCertificate != null)
+ if (peerCertificate != null)
{
throw new TlsFatalAlert(AlertDescription.unexpected_message);
}
@@ -432,21 +441,31 @@ public class TlsServerProtocol
// Verify the CertificateVerify message contains a correct signature.
try
{
- // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned
- byte[] certificateVerifyHash = getCurrentPRFHash(getContext(), prepareFinishHash, null);
+ byte[] hash;
+ if (TlsUtils.isTLSv12(getContext()))
+ {
+ hash = prepareFinishHash.getFinalHash(clientCertificateVerify.getAlgorithm().getHash());
+ }
+ else
+ {
+ hash = securityParameters.getSessionHash();
+ }
- org.bouncycastle.asn1.x509.Certificate x509Cert = this.peerCertificate.getCertificateAt(0);
+ org.bouncycastle.asn1.x509.Certificate x509Cert = peerCertificate.getCertificateAt(0);
SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
- TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType);
+ TlsSigner tlsSigner = TlsUtils.createTlsSigner(clientCertificateType);
tlsSigner.init(getContext());
- tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
- clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash);
+ if (!tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
+ clientCertificateVerify.getSignature(), publicKey, hash))
+ {
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
}
catch (Exception e)
{
- throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ throw new TlsFatalAlert(AlertDescription.decrypt_error, e);
}
}
@@ -454,6 +473,8 @@ public class TlsServerProtocol
throws IOException
{
ProtocolVersion client_version = TlsUtils.readVersion(buf);
+ recordStream.setWriteVersion(client_version);
+
if (client_version.isDTLS())
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
@@ -501,9 +522,12 @@ public class TlsServerProtocol
*/
this.clientExtensions = readExtensions(buf);
- getContext().setClientVersion(client_version);
+ this.securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(clientExtensions);
+
+ getContextAdmin().setClientVersion(client_version);
tlsServer.notifyClientVersion(client_version);
+ tlsServer.notifyFallback(Arrays.contains(offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV));
securityParameters.clientRandom = client_random;
@@ -562,15 +586,16 @@ public class TlsServerProtocol
protected void receiveClientKeyExchangeMessage(ByteArrayInputStream buf)
throws IOException
{
- this.keyExchange.processClientKeyExchange(buf);
+ keyExchange.processClientKeyExchange(buf);
assertEmpty(buf);
+ this.prepareFinishHash = recordStream.prepareToFinish();
+ this.securityParameters.sessionHash = getCurrentPRFHash(getContext(), prepareFinishHash, null);
+
establishMasterSecret(getContext(), keyExchange);
recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
- this.prepareFinishHash = recordStream.prepareToFinish();
-
if (!expectSessionTicket)
{
sendChangeCipherSpecMessage();
@@ -626,7 +651,7 @@ public class TlsServerProtocol
recordStream.setReadVersion(server_version);
recordStream.setWriteVersion(server_version);
recordStream.setRestrictReadVersion(true);
- getContext().setServerVersion(server_version);
+ getContextAdmin().setServerVersion(server_version);
TlsUtils.writeVersion(server_version, message);
@@ -639,16 +664,17 @@ public class TlsServerProtocol
TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, message);
int selectedCipherSuite = tlsServer.getSelectedCipherSuite();
- if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite)
+ if (!Arrays.contains(offeredCipherSuites, selectedCipherSuite)
|| selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
- || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+ || CipherSuite.isSCSV(selectedCipherSuite)
+ || !TlsUtils.isValidCipherSuiteForVersion(selectedCipherSuite, server_version))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
securityParameters.cipherSuite = selectedCipherSuite;
short selectedCompressionMethod = tlsServer.getSelectedCompressionMethod();
- if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod))
+ if (!Arrays.contains(offeredCompressionMethods, selectedCompressionMethod))
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
@@ -681,11 +707,17 @@ public class TlsServerProtocol
* 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 = TlsExtensionsUtils.ensureExtensionsInitialised(serverExtensions);
this.serverExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
}
}
+ if (securityParameters.extendedMasterSecret)
+ {
+ this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(serverExtensions);
+ TlsExtensionsUtils.addExtendedMasterSecretExtension(serverExtensions);
+ }
+
/*
* 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
@@ -694,29 +726,31 @@ public class TlsServerProtocol
if (this.serverExtensions != null)
{
+ this.securityParameters.encryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(serverExtensions);
+
this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(clientExtensions,
- this.serverExtensions, AlertDescription.internal_error);
+ serverExtensions, AlertDescription.internal_error);
- this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(this.serverExtensions);
+ this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(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,
+ this.allowCertificateStatus = !resumedSession
+ && TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, TlsExtensionsUtils.EXT_status_request,
AlertDescription.internal_error);
- this.expectSessionTicket = !this.resumedSession
- && TlsUtils.hasExpectedEmptyExtensionData(this.serverExtensions, TlsProtocol.EXT_SessionTicket,
+ this.expectSessionTicket = !resumedSession
+ && TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, TlsProtocol.EXT_SessionTicket,
AlertDescription.internal_error);
- writeExtensions(message, this.serverExtensions);
+ writeExtensions(message, serverExtensions);
}
- if (this.securityParameters.maxFragmentLength >= 0)
+ if (securityParameters.maxFragmentLength >= 0)
{
- int plainTextLimit = 1 << (8 + this.securityParameters.maxFragmentLength);
+ int plainTextLimit = 1 << (8 + securityParameters.maxFragmentLength);
recordStream.setPlaintextLimit(plainTextLimit);
}
@@ -730,7 +764,7 @@ public class TlsServerProtocol
message.writeToRecordStream();
- this.recordStream.notifyHelloComplete();
+ recordStream.notifyHelloComplete();
}
protected void sendServerHelloDoneMessage()
@@ -755,6 +789,6 @@ public class TlsServerProtocol
protected boolean expectCertificateVerifyMessage()
{
- return this.clientCertificateType >= 0 && TlsUtils.hasSigningCapability(this.clientCertificateType);
+ return clientCertificateType >= 0 && TlsUtils.hasSigningCapability(clientCertificateType);
}
}
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 178731d3..4e2c2639 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
@@ -6,13 +6,12 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.StreamCipher;
import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
public class TlsStreamCipher
implements TlsCipher
{
- private static boolean encryptThenMAC = false;
-
protected TlsContext context;
protected StreamCipher encryptCipher;
@@ -21,13 +20,16 @@ public class TlsStreamCipher
protected TlsMac writeMac;
protected TlsMac readMac;
+ protected boolean usesNonce;
+
public TlsStreamCipher(TlsContext context, StreamCipher clientWriteCipher,
StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest,
- int cipherKeySize) throws IOException
+ int cipherKeySize, boolean usesNonce) throws IOException
{
boolean isServer = context.isServer();
this.context = context;
+ this.usesNonce = usesNonce;
this.encryptCipher = clientWriteCipher;
this.decryptCipher = serverWriteCipher;
@@ -78,6 +80,13 @@ public class TlsStreamCipher
decryptParams = serverWriteKey;
}
+ if (usesNonce)
+ {
+ byte[] dummyNonce = new byte[8];
+ encryptParams = new ParametersWithIV(encryptParams, dummyNonce);
+ decryptParams = new ParametersWithIV(decryptParams, dummyNonce);
+ }
+
this.encryptCipher.init(true, encryptParams);
this.decryptCipher.init(false, decryptParams);
}
@@ -90,26 +99,22 @@ public class TlsStreamCipher
public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
{
/*
- * TODO[draft-josefsson-salsa20-tls-02] Note that Salsa20 requires a 64-bit nonce. That
+ * draft-josefsson-salsa20-tls-04 2.1 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.
*/
+ if (usesNonce)
+ {
+ updateIV(encryptCipher, true, seqNo);
+ }
byte[] outBuf = new byte[len + writeMac.getSize()];
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);
- }
+ byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+ encryptCipher.processBytes(mac, 0, mac.length, outBuf, len);
return outBuf;
}
@@ -118,11 +123,15 @@ public class TlsStreamCipher
throws IOException
{
/*
- * TODO[draft-josefsson-salsa20-tls-02] Note that Salsa20 requires a 64-bit nonce. That
+ * draft-josefsson-salsa20-tls-04 2.1 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.
*/
+ if (usesNonce)
+ {
+ updateIV(decryptCipher, false, seqNo);
+ }
int macSize = readMac.getSize();
if (len < macSize)
@@ -132,24 +141,13 @@ public class TlsStreamCipher
int plaintextLength = 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[] 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);
}
- private void checkMAC(long seqNo, short type, byte[] recBuf, int recStart, int recEnd, byte[] calcBuf, int calcOff, int calcLen)
+ protected 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);
@@ -160,4 +158,11 @@ public class TlsStreamCipher
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
}
+
+ protected void updateIV(StreamCipher cipher, boolean forEncryption, long seqNo)
+ {
+ byte[] nonce = new byte[8];
+ TlsUtils.writeUint64(seqNo, nonce, 0);
+ cipher.init(forEncryption, new ParametersWithIV(null, nonce));
+ }
}
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 dae9ff5f..d3909834 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
@@ -43,7 +43,10 @@ import org.bouncycastle.util.io.Streams;
*/
public class TlsUtils
{
- public static byte[] EMPTY_BYTES = new byte[0];
+ public static final byte[] EMPTY_BYTES = new byte[0];
+ public static final short[] EMPTY_SHORTS = new short[0];
+ public static final int[] EMPTY_INTS = new int[0];
+ public static final long[] EMPTY_LONGS = new long[0];
public static final Integer EXT_signature_algorithms = Integers.valueOf(ExtensionType.signature_algorithms);
@@ -63,6 +66,14 @@ public class TlsUtils
}
}
+ public static void checkUint8(long i) throws IOException
+ {
+ if (!isValidUint8(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
public static void checkUint16(int i) throws IOException
{
if (!isValidUint16(i))
@@ -71,6 +82,14 @@ public class TlsUtils
}
}
+ public static void checkUint16(long i) throws IOException
+ {
+ if (!isValidUint16(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
public static void checkUint24(int i) throws IOException
{
if (!isValidUint24(i))
@@ -79,6 +98,14 @@ public class TlsUtils
}
}
+ public static void checkUint24(long i) throws IOException
+ {
+ if (!isValidUint24(i))
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
public static void checkUint32(long i) throws IOException
{
if (!isValidUint32(i))
@@ -113,16 +140,31 @@ public class TlsUtils
return (i & 0xFF) == i;
}
+ public static boolean isValidUint8(long i)
+ {
+ return (i & 0xFFL) == i;
+ }
+
public static boolean isValidUint16(int i)
{
return (i & 0xFFFF) == i;
}
+ public static boolean isValidUint16(long i)
+ {
+ return (i & 0xFFFFL) == i;
+ }
+
public static boolean isValidUint24(int i)
{
return (i & 0xFFFFFF) == i;
}
+ public static boolean isValidUint24(long i)
+ {
+ return (i & 0xFFFFFFL) == i;
+ }
+
public static boolean isValidUint32(long i)
{
return (i & 0xFFFFFFFFL) == i;
@@ -178,92 +220,92 @@ public class TlsUtils
public static void writeUint16(int i, OutputStream output)
throws IOException
{
- output.write(i >> 8);
+ output.write(i >>> 8);
output.write(i);
}
public static void writeUint16(int i, byte[] buf, int offset)
{
- buf[offset] = (byte)(i >> 8);
+ buf[offset] = (byte)(i >>> 8);
buf[offset + 1] = (byte)i;
}
public static void writeUint24(int i, OutputStream output)
throws IOException
{
- output.write(i >> 16);
- output.write(i >> 8);
- output.write(i);
+ output.write((byte)(i >>> 16));
+ output.write((byte)(i >>> 8));
+ output.write((byte)i);
}
public static void writeUint24(int i, byte[] buf, int offset)
{
- buf[offset] = (byte)(i >> 16);
- buf[offset + 1] = (byte)(i >> 8);
- buf[offset + 2] = (byte)(i);
+ buf[offset] = (byte)(i >>> 16);
+ buf[offset + 1] = (byte)(i >>> 8);
+ buf[offset + 2] = (byte)i;
}
public static void writeUint32(long i, OutputStream output)
throws IOException
{
- output.write((int)(i >> 24));
- output.write((int)(i >> 16));
- output.write((int)(i >> 8));
- output.write((int)(i));
+ output.write((byte)(i >>> 24));
+ output.write((byte)(i >>> 16));
+ output.write((byte)(i >>> 8));
+ output.write((byte)i);
}
public static void writeUint32(long i, byte[] buf, int offset)
{
- buf[offset] = (byte)(i >> 24);
- buf[offset + 1] = (byte)(i >> 16);
- buf[offset + 2] = (byte)(i >> 8);
- buf[offset + 3] = (byte)(i);
+ buf[offset] = (byte)(i >>> 24);
+ buf[offset + 1] = (byte)(i >>> 16);
+ buf[offset + 2] = (byte)(i >>> 8);
+ 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));
+ 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);
- buf[offset + 1] = (byte)(i >> 32);
- buf[offset + 2] = (byte)(i >> 24);
- buf[offset + 3] = (byte)(i >> 16);
- buf[offset + 4] = (byte)(i >> 8);
- buf[offset + 5] = (byte)(i);
+ buf[offset] = (byte)(i >>> 40);
+ buf[offset + 1] = (byte)(i >>> 32);
+ buf[offset + 2] = (byte)(i >>> 24);
+ buf[offset + 3] = (byte)(i >>> 16);
+ buf[offset + 4] = (byte)(i >>> 8);
+ buf[offset + 5] = (byte)i;
}
public static void writeUint64(long i, OutputStream output)
throws IOException
{
- 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));
+ 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)
{
- buf[offset] = (byte)(i >> 56);
- buf[offset + 1] = (byte)(i >> 48);
- buf[offset + 2] = (byte)(i >> 40);
- buf[offset + 3] = (byte)(i >> 32);
- buf[offset + 4] = (byte)(i >> 24);
- buf[offset + 5] = (byte)(i >> 16);
- buf[offset + 6] = (byte)(i >> 8);
- buf[offset + 7] = (byte)(i);
+ buf[offset] = (byte)(i >>> 56);
+ buf[offset + 1] = (byte)(i >>> 48);
+ buf[offset + 2] = (byte)(i >>> 40);
+ buf[offset + 3] = (byte)(i >>> 32);
+ buf[offset + 4] = (byte)(i >>> 24);
+ buf[offset + 5] = (byte)(i >>> 16);
+ buf[offset + 6] = (byte)(i >>> 8);
+ buf[offset + 7] = (byte)i;
}
public static void writeOpaque8(byte[] buf, OutputStream output)
@@ -451,23 +493,24 @@ public class TlsUtils
{
throw new EOFException();
}
- return (((long)i1) << 24) | (((long)i2) << 16) | (((long)i3) << 8) | ((long)i4);
+ return ((i1 << 2) | (i2 << 16) | (i3 << 8) | i4) & 0xFFFFFFFFL;
+ }
+
+ public static long readUint32(byte[] buf, int offset)
+ {
+ int n = (buf[offset] & 0xff) << 24;
+ n |= (buf[++offset] & 0xff) << 16;
+ n |= (buf[++offset] & 0xff) << 8;
+ n |= (buf[++offset] & 0xff);
+ return n & 0xFFFFFFFFL;
}
public static long readUint48(InputStream input)
throws IOException
{
- int i1 = input.read();
- int i2 = input.read();
- int i3 = input.read();
- int i4 = input.read();
- int i5 = input.read();
- int i6 = input.read();
- if (i6 < 0)
- {
- throw new EOFException();
- }
- return (((long)i1) << 40) | (((long)i2) << 32) | (((long)i3) << 24) | (((long)i4) << 16) | (((long)i5) << 8) | ((long)i6);
+ int hi = readUint24(input);
+ int lo = readUint24(input);
+ return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL);
}
public static long readUint48(byte[] buf, int offset)
@@ -634,9 +677,9 @@ public class TlsUtils
public static void writeGMTUnixTime(byte[] buf, int offset)
{
int t = (int)(System.currentTimeMillis() / 1000L);
- buf[offset] = (byte)(t >> 24);
- buf[offset + 1] = (byte)(t >> 16);
- buf[offset + 2] = (byte)(t >> 8);
+ buf[offset] = (byte)(t >>> 24);
+ buf[offset + 1] = (byte)(t >>> 16);
+ buf[offset + 2] = (byte)(t >>> 8);
buf[offset + 3] = (byte)t;
}
@@ -668,6 +711,40 @@ public class TlsUtils
return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa));
}
+ public static Vector getDefaultSupportedSignatureAlgorithms()
+ {
+ short[] hashAlgorithms = new short[]{ HashAlgorithm.sha1, HashAlgorithm.sha224, HashAlgorithm.sha256,
+ HashAlgorithm.sha384, HashAlgorithm.sha512 };
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa, SignatureAlgorithm.dsa,
+ SignatureAlgorithm.ecdsa };
+
+ Vector result = new Vector();
+ for (int i = 0; i < signatureAlgorithms.length; ++i)
+ {
+ for (int j = 0; j < hashAlgorithms.length; ++j)
+ {
+ result.addElement(new SignatureAndHashAlgorithm(hashAlgorithms[j], signatureAlgorithms[i]));
+ }
+ }
+ return result;
+ }
+
+ public static SignatureAndHashAlgorithm getSignatureAndHashAlgorithm(TlsContext context,
+ TlsSignerCredentials signerCredentials)
+ throws IOException
+ {
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ if (TlsUtils.isTLSv12(context))
+ {
+ signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm();
+ if (signatureAndHashAlgorithm == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+ return signatureAndHashAlgorithm;
+ }
+
public static byte[] getExtensionData(Hashtable extensions, Integer extensionType)
{
return extensions == null ? null : (byte[])extensions.get(extensionType);
@@ -780,8 +857,8 @@ public class TlsUtils
// supported_signature_algorithms
int length = 2 * supportedSignatureAlgorithms.size();
- TlsUtils.checkUint16(length);
- TlsUtils.writeUint16(length, output);
+ checkUint16(length);
+ writeUint16(length, output);
for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
{
SignatureAndHashAlgorithm entry = (SignatureAndHashAlgorithm)supportedSignatureAlgorithms.elementAt(i);
@@ -802,7 +879,7 @@ public class TlsUtils
throws IOException
{
// supported_signature_algorithms
- int length = TlsUtils.readUint16(input);
+ int length = readUint16(input);
if (length < 2 || (length & 1) != 0)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
@@ -850,6 +927,14 @@ public class TlsUtils
return buf;
}
+ public static byte[] PRF_legacy(byte[] secret, String asciiLabel, byte[] seed, int size)
+ {
+ byte[] label = Strings.toByteArray(asciiLabel);
+ byte[] labelSeed = concat(label, seed);
+
+ return PRF_legacy(secret, label, labelSeed, size);
+ }
+
static byte[] PRF_legacy(byte[] secret, byte[] label, byte[] labelSeed, int size)
{
int s_half = (secret.length + 1) / 2;
@@ -860,8 +945,8 @@ public class TlsUtils
byte[] b1 = new byte[size];
byte[] b2 = new byte[size];
- hmac_hash(new MD5Digest(), s1, labelSeed, b1);
- hmac_hash(new SHA1Digest(), s2, labelSeed, b2);
+ hmac_hash(createHash(HashAlgorithm.md5), s1, labelSeed, b1);
+ hmac_hash(createHash(HashAlgorithm.sha1), s2, labelSeed, b2);
for (int i = 0; i < size; i++)
{
b1[i] ^= b2[i];
@@ -880,7 +965,7 @@ public class TlsUtils
static void hmac_hash(Digest digest, byte[] secret, byte[] seed, byte[] out)
{
HMac mac = new HMac(digest);
- KeyParameter param = new KeyParameter(secret);
+ mac.init(new KeyParameter(secret));
byte[] a = seed;
int size = digest.getDigestSize();
int iterations = (out.length + size - 1) / size;
@@ -888,11 +973,9 @@ public class TlsUtils
byte[] buf2 = new byte[mac.getMacSize()];
for (int i = 0; i < iterations; i++)
{
- mac.init(param);
mac.update(a, 0, a.length);
mac.doFinal(buf, 0);
a = buf;
- mac.init(param);
mac.update(a, 0, a.length);
mac.update(seed, 0, seed.length);
mac.doFinal(buf2, 0);
@@ -935,8 +1018,8 @@ public class TlsUtils
static byte[] calculateKeyBlock_SSL(byte[] master_secret, byte[] random, int size)
{
- Digest md5 = new MD5Digest();
- Digest sha1 = new SHA1Digest();
+ Digest md5 = createHash(HashAlgorithm.md5);
+ Digest sha1 = createHash(HashAlgorithm.sha1);
int md5Size = md5.getDigestSize();
byte[] shatmp = new byte[sha1.getDigestSize()];
byte[] tmp = new byte[size + md5Size];
@@ -959,28 +1042,39 @@ public class TlsUtils
++i;
}
- byte rval[] = new byte[size];
- System.arraycopy(tmp, 0, rval, 0, size);
- return rval;
+ return Arrays.copyOfRange(tmp, 0, size);
}
static byte[] calculateMasterSecret(TlsContext context, byte[] pre_master_secret)
{
SecurityParameters securityParameters = context.getSecurityParameters();
- byte[] seed = concat(securityParameters.getClientRandom(), securityParameters.getServerRandom());
+
+ byte[] seed;
+ if (securityParameters.extendedMasterSecret)
+ {
+ seed = securityParameters.getSessionHash();
+ }
+ else
+ {
+ seed = concat(securityParameters.getClientRandom(), securityParameters.getServerRandom());
+ }
if (isSSL(context))
{
return calculateMasterSecret_SSL(pre_master_secret, seed);
}
- return PRF(context, pre_master_secret, ExporterLabel.master_secret, seed, 48);
+ String asciiLabel = securityParameters.extendedMasterSecret
+ ? ExporterLabel.extended_master_secret
+ : ExporterLabel.master_secret;
+
+ return PRF(context, pre_master_secret, asciiLabel, seed, 48);
}
static byte[] calculateMasterSecret_SSL(byte[] pre_master_secret, byte[] random)
{
- Digest md5 = new MD5Digest();
- Digest sha1 = new SHA1Digest();
+ Digest md5 = createHash(HashAlgorithm.md5);
+ Digest sha1 = createHash(HashAlgorithm.sha1);
int md5Size = md5.getDigestSize();
byte[] shatmp = new byte[sha1.getDigestSize()];
@@ -1020,7 +1114,7 @@ public class TlsUtils
return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length);
}
- public static final Digest createHash(short hashAlgorithm)
+ public static Digest createHash(short hashAlgorithm)
{
switch (hashAlgorithm)
{
@@ -1041,7 +1135,14 @@ public class TlsUtils
}
}
- public static final Digest cloneHash(short hashAlgorithm, Digest hash)
+ public static Digest createHash(SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ return signatureAndHashAlgorithm == null
+ ? new CombinedHash()
+ : createHash(signatureAndHashAlgorithm.getHash());
+ }
+
+ public static Digest cloneHash(short hashAlgorithm, Digest hash)
{
switch (hashAlgorithm)
{
@@ -1062,7 +1163,7 @@ public class TlsUtils
}
}
- public static final Digest createPRFHash(int prfAlgorithm)
+ public static Digest createPRFHash(int prfAlgorithm)
{
switch (prfAlgorithm)
{
@@ -1073,7 +1174,7 @@ public class TlsUtils
}
}
- public static final Digest clonePRFHash(int prfAlgorithm, Digest hash)
+ public static Digest clonePRFHash(int prfAlgorithm, Digest hash)
{
switch (prfAlgorithm)
{
@@ -1084,7 +1185,7 @@ public class TlsUtils
}
}
- public static final short getHashAlgorithmForPRFAlgorithm(int prfAlgorithm)
+ public static short getHashAlgorithmForPRFAlgorithm(int prfAlgorithm)
{
switch (prfAlgorithm)
{
@@ -1183,12 +1284,12 @@ public class TlsUtils
// TODO Add support for ClientCertificateType.*_fixed_*
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
}
catch (Exception e)
{
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
}
-
- throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
}
static void trackHashAlgorithms(TlsHandshakeHash handshakeHash, Vector supportedSignatureAlgorithms)
@@ -1237,9 +1338,9 @@ public class TlsUtils
static final byte[] SSL_SERVER = {0x53, 0x52, 0x56, 0x52};
// SSL3 magic mix constants ("A", "BB", "CCC", ...)
- static final byte[][] SSL3_CONST = genConst();
+ static final byte[][] SSL3_CONST = genSSL3Const();
- private static byte[][] genConst()
+ private static byte[][] genSSL3Const()
{
int n = 10;
byte[][] arr = new byte[n][];
@@ -1258,4 +1359,485 @@ public class TlsUtils
v.addElement(obj);
return v;
}
+
+ public static int getCipherType(int ciphersuite) throws IOException
+ {
+ switch (getEncryptionAlgorithm(ciphersuite))
+ {
+ case EncryptionAlgorithm.AES_128_GCM:
+ case EncryptionAlgorithm.AES_256_GCM:
+ case EncryptionAlgorithm.AES_128_CCM:
+ case EncryptionAlgorithm.AES_128_CCM_8:
+ case EncryptionAlgorithm.AES_256_CCM:
+ case EncryptionAlgorithm.AES_256_CCM_8:
+ case EncryptionAlgorithm.CAMELLIA_128_GCM:
+ case EncryptionAlgorithm.CAMELLIA_256_GCM:
+ case EncryptionAlgorithm.AEAD_CHACHA20_POLY1305:
+ return CipherType.aead;
+
+ case EncryptionAlgorithm.RC2_CBC_40:
+ case EncryptionAlgorithm.IDEA_CBC:
+ case EncryptionAlgorithm.DES40_CBC:
+ case EncryptionAlgorithm.DES_CBC:
+ case EncryptionAlgorithm._3DES_EDE_CBC:
+ case EncryptionAlgorithm.AES_128_CBC:
+ case EncryptionAlgorithm.AES_256_CBC:
+ case EncryptionAlgorithm.CAMELLIA_128_CBC:
+ case EncryptionAlgorithm.CAMELLIA_256_CBC:
+ case EncryptionAlgorithm.SEED_CBC:
+ return CipherType.block;
+
+ case EncryptionAlgorithm.RC4_40:
+ case EncryptionAlgorithm.RC4_128:
+ case EncryptionAlgorithm.ESTREAM_SALSA20:
+ case EncryptionAlgorithm.SALSA20:
+ return CipherType.stream;
+
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public static int getEncryptionAlgorithm(int ciphersuite) throws IOException
+ {
+ switch (ciphersuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_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_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ return EncryptionAlgorithm._3DES_EDE_CBC;
+
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return EncryptionAlgorithm.AEAD_CHACHA20_POLY1305;
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_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_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ return EncryptionAlgorithm.AES_128_CBC;
+
+ 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_PSK_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_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ return EncryptionAlgorithm.AES_128_CBC;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ return EncryptionAlgorithm.AES_128_CCM;
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ return EncryptionAlgorithm.AES_128_CCM_8;
+
+ 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:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ return EncryptionAlgorithm.AES_128_GCM;
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_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_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ return EncryptionAlgorithm.AES_256_CBC;
+
+ 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:
+ return EncryptionAlgorithm.AES_256_CBC;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ 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_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_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 EncryptionAlgorithm.AES_256_CBC;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ return EncryptionAlgorithm.AES_256_CCM;
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ return EncryptionAlgorithm.AES_256_CCM_8;
+
+ 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_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_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 EncryptionAlgorithm.AES_256_GCM;
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ return EncryptionAlgorithm.CAMELLIA_128_CBC;
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ return EncryptionAlgorithm.CAMELLIA_128_CBC;
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ return EncryptionAlgorithm.CAMELLIA_128_GCM;
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ return EncryptionAlgorithm.CAMELLIA_256_CBC;
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ return EncryptionAlgorithm.CAMELLIA_256_CBC;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ return EncryptionAlgorithm.CAMELLIA_256_CBC;
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ return EncryptionAlgorithm.CAMELLIA_256_GCM;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ return EncryptionAlgorithm.ESTREAM_SALSA20;
+
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ return EncryptionAlgorithm.NULL;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ return EncryptionAlgorithm.NULL;
+
+ 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:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ return EncryptionAlgorithm.NULL;
+
+ 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 EncryptionAlgorithm.NULL;
+
+ case CipherSuite.TLS_DH_anon_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ return EncryptionAlgorithm.RC4_128;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ return EncryptionAlgorithm.RC4_128;
+
+ case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ return EncryptionAlgorithm.SALSA20;
+
+ 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:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return EncryptionAlgorithm.SEED_CBC;
+
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public static ProtocolVersion getMinimumVersion(int ciphersuite)
+ {
+ switch (ciphersuite)
+ {
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_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_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ 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_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ 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_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ 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_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ 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_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ 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_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_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_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ 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_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_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ return ProtocolVersion.TLSv12;
+
+ default:
+ return ProtocolVersion.SSLv3;
+ }
+ }
+
+ public static boolean isAEADCipherSuite(int ciphersuite) throws IOException
+ {
+ return CipherType.aead == getCipherType(ciphersuite);
+ }
+
+ public static boolean isBlockCipherSuite(int ciphersuite) throws IOException
+ {
+ return CipherType.block == getCipherType(ciphersuite);
+ }
+
+ public static boolean isStreamCipherSuite(int ciphersuite) throws IOException
+ {
+ return CipherType.stream == getCipherType(ciphersuite);
+ }
+
+ public static boolean isValidCipherSuiteForVersion(int cipherSuite, ProtocolVersion serverVersion)
+ {
+ return getMinimumVersion(cipherSuite).isEqualOrEarlierVersionOf(serverVersion.getEquivalentTLSVersion());
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java
index 8ecfce00..7a83e4d8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/UseSRTPData.java
@@ -5,9 +5,8 @@ package org.bouncycastle.crypto.tls;
*/
public class UseSRTPData
{
-
- private int[] protectionProfiles;
- private byte[] mki;
+ protected int[] protectionProfiles;
+ protected byte[] mki;
/**
* @param protectionProfiles see {@link SRTPProtectionProfile} for valid constants.
@@ -15,7 +14,6 @@ public class UseSRTPData
*/
public UseSRTPData(int[] protectionProfiles, byte[] mki)
{
-
if (protectionProfiles == null || protectionProfiles.length < 1
|| protectionProfiles.length >= (1 << 15))
{
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/AllTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/AllTests.java
new file mode 100644
index 00000000..ef5b29bc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/AllTests.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.crypto.tls.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests
+{
+ public static void main(String[] args)
+ throws Exception
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static Test suite()
+ throws Exception
+ {
+ TestSuite suite = new TestSuite("TLS tests");
+
+ suite.addTest(BasicTlsTest.suite());
+
+ return suite;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/BasicTlsTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/BasicTlsTest.java
new file mode 100644
index 00000000..49f63108
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/BasicTlsTest.java
@@ -0,0 +1,226 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.Certificate;
+import org.bouncycastle.crypto.tls.CipherSuite;
+import org.bouncycastle.crypto.tls.DefaultTlsClient;
+import org.bouncycastle.crypto.tls.ServerOnlyTlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsClient;
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+import org.bouncycastle.crypto.tls.TlsFatalAlert;
+import org.bouncycastle.crypto.tls.TlsKeyExchange;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+public class BasicTlsTest
+ extends TestCase
+{
+ private static final int PORT_NO = 8003;
+
+ protected boolean isSufficientVMVersion(String vmVersion)
+ {
+ if (vmVersion == null)
+ {
+ return false;
+ }
+ String[] parts = vmVersion.split("\\.");
+ if (parts == null || parts.length != 2)
+ {
+ return false;
+ }
+ try
+ {
+ int major = Integer.parseInt(parts[0]);
+ if (major != 1)
+ {
+ return major > 1;
+ }
+ int minor = Integer.parseInt(parts[1]);
+ return minor >= 7;
+ }
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+ public void testConnection()
+ throws Exception
+ {
+ String vmVersion = System.getProperty("java.specification.version");
+ if (!isSufficientVMVersion(vmVersion))
+ {
+ return; // only works on later VMs.
+ }
+
+ Thread server = new HTTPSServerThread();
+
+ server.start();
+
+ Thread.yield();
+
+ Socket s = null;
+
+ for (int i = 0; s == null && i != 3; i++)
+ {
+ Thread.sleep(1000);
+
+ try
+ {
+ s = new Socket("localhost", PORT_NO);
+ }
+ catch (IOException e)
+ {
+ // ignore
+ }
+ }
+
+ if (s == null)
+ {
+ throw new IOException("unable to connect");
+ }
+
+ TlsClientProtocol protocol = new TlsClientProtocol(s.getInputStream(), s.getOutputStream(),
+ new SecureRandom());
+ protocol.connect(new MyTlsClient(new ServerOnlyTlsAuthentication()
+ {
+ public void notifyServerCertificate(Certificate serverCertificate) throws IOException
+ {
+ // NOTE: In production code this MUST verify the certificate!
+ }
+ }));
+
+ InputStream is = protocol.getInputStream();
+ OutputStream os = protocol.getOutputStream();
+
+ os.write("GET / HTTP/1.1\r\n\r\n".getBytes());
+
+ byte[] buf = new byte[4096];
+ int read = 0;
+ int total = 0;
+
+ while ((read = is.read(buf, total, buf.length - total)) > 0)
+ {
+ total += read;
+ }
+
+ is.close();
+
+ byte[] expected = Hex.decode("485454502f312e3120323030204f4b0d0a436f6e74656e742d547970653a20746578742f68"
+ + "746d6c0d0a0d0a3c68746d6c3e0d0a3c626f64793e0d0a48656c6c6f20576f726c64210d0a3c2f626f64793e0d0a3c2f"
+ + "68746d6c3e0d0a");
+ assertEquals(total, expected.length);
+
+ byte[] tmp = new byte[expected.length];
+ System.arraycopy(buf, 0, tmp, 0, total);
+ assertTrue(Arrays.areEqual(expected, tmp));
+ }
+
+ public void testRSAConnectionClient()
+ throws Exception
+ {
+ MyTlsClient client = new MyTlsClient(null);
+
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, TlsTestUtils.rsaCertData);
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, TlsTestUtils.rsaCertData);
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, TlsTestUtils.rsaCertData);
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_RC4_128_SHA, TlsTestUtils.rsaCertData);
+
+ try
+ {
+ checkConnectionClient(client, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, TlsTestUtils.dudRsaCertData);
+
+ fail("dud certificate not caught");
+ }
+ catch (TlsFatalAlert e)
+ {
+ assertEquals(AlertDescription.certificate_unknown, e.getAlertDescription());
+ }
+
+ try
+ {
+ checkConnectionClient(client, CipherSuite.TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, TlsTestUtils.rsaCertData);
+
+ fail("wrong certificate not caught");
+ }
+ catch (TlsFatalAlert e)
+ {
+ assertEquals(AlertDescription.internal_error, e.getAlertDescription());
+ }
+ }
+
+ private void checkConnectionClient(TlsClient client, int cipherSuite, byte[] encCert)
+ throws Exception
+ {
+ client.notifySelectedCipherSuite(cipherSuite);
+
+ TlsKeyExchange keyExchange = client.getKeyExchange();
+
+ keyExchange
+ .processServerCertificate(new Certificate(
+ new org.bouncycastle.asn1.x509.Certificate[]{org.bouncycastle.asn1.x509.Certificate
+ .getInstance(encCert)}));
+ }
+
+ public static TestSuite suite()
+ {
+ return new TestSuite(BasicTlsTest.class);
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ static class MyTlsClient
+ extends DefaultTlsClient
+ {
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client raised alert (AlertLevel." + alertLevel + ", AlertDescription." + alertDescription
+ + ")");
+ if (message != null)
+ {
+ out.println(message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client received alert (AlertLevel." + alertLevel + ", AlertDescription."
+ + alertDescription + ")");
+ }
+
+ private final TlsAuthentication authentication;
+
+ MyTlsClient(TlsAuthentication authentication)
+ {
+ this.authentication = authentication;
+ }
+
+ public TlsAuthentication getAuthentication()
+ throws IOException
+ {
+ return authentication;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSClientTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSClientTest.java
new file mode 100644
index 00000000..37ad8caf
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSClientTest.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.DTLSClientProtocol;
+import org.bouncycastle.crypto.tls.DTLSTransport;
+import org.bouncycastle.crypto.tls.DatagramTransport;
+import org.bouncycastle.crypto.tls.TlsClient;
+import org.bouncycastle.crypto.tls.TlsSession;
+import org.bouncycastle.crypto.tls.UDPTransport;
+
+/**
+ * A simple test designed to conduct a DTLS handshake with an external DTLS server.
+ * <p>
+ * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+ * this package (under 'src/test/resources') for help configuring an external DTLS server.
+ * </p>
+ */
+public class DTLSClientTest
+{
+ private static final SecureRandom secureRandom = new SecureRandom();
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ InetAddress address = InetAddress.getLocalHost();
+ int port = 5556;
+
+ TlsSession session = createSession(address, port);
+
+ MockDTLSClient client = new MockDTLSClient(session);
+
+ DTLSTransport dtls = openDTLSConnection(address, port, client);
+
+ System.out.println("Receive limit: " + dtls.getReceiveLimit());
+ System.out.println("Send limit: " + dtls.getSendLimit());
+
+ // Send and hopefully receive a packet back
+
+ byte[] request = "Hello World!\n".getBytes("UTF-8");
+ dtls.send(request, 0, request.length);
+
+ byte[] response = new byte[dtls.getReceiveLimit()];
+ int received = dtls.receive(response, 0, response.length, 30000);
+ if (received >= 0)
+ {
+ System.out.println(new String(response, 0, received, "UTF-8"));
+ }
+
+ dtls.close();
+ }
+
+ private static TlsSession createSession(InetAddress address, int port)
+ throws IOException
+ {
+ MockDTLSClient client = new MockDTLSClient(null);
+ DTLSTransport dtls = openDTLSConnection(address, port, client);
+ TlsSession session = client.getSessionToResume();
+ dtls.close();
+ return session;
+ }
+
+ private static DTLSTransport openDTLSConnection(InetAddress address, int port, TlsClient client)
+ throws IOException
+ {
+ DatagramSocket socket = new DatagramSocket();
+ socket.connect(address, port);
+
+ int mtu = 1500;
+ DatagramTransport transport = new UDPTransport(socket, mtu);
+ transport = new UnreliableDatagramTransport(transport, secureRandom, 0, 0);
+ transport = new LoggingDatagramTransport(transport, System.out);
+
+ DTLSClientProtocol protocol = new DTLSClientProtocol(secureRandom);
+
+ return protocol.connect(client, transport);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSProtocolTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSProtocolTest.java
new file mode 100644
index 00000000..df5a3f87
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSProtocolTest.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+
+import org.bouncycastle.crypto.tls.DTLSClientProtocol;
+import org.bouncycastle.crypto.tls.DTLSServerProtocol;
+import org.bouncycastle.crypto.tls.DTLSTransport;
+import org.bouncycastle.crypto.tls.DatagramTransport;
+import org.bouncycastle.util.Arrays;
+
+public class DTLSProtocolTest
+ extends TestCase
+{
+ public void testClientServer()
+ throws Exception
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ DTLSClientProtocol clientProtocol = new DTLSClientProtocol(secureRandom);
+ DTLSServerProtocol serverProtocol = new DTLSServerProtocol(secureRandom);
+
+ MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+ ServerThread serverThread = new ServerThread(serverProtocol, network.getServer());
+ serverThread.start();
+
+ DatagramTransport clientTransport = network.getClient();
+
+ clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0);
+
+ clientTransport = new LoggingDatagramTransport(clientTransport, System.out);
+
+ MockDTLSClient client = new MockDTLSClient(null);
+
+ DTLSTransport dtlsClient = clientProtocol.connect(client, clientTransport);
+
+ for (int i = 1; i <= 10; ++i)
+ {
+ byte[] data = new byte[i];
+ Arrays.fill(data, (byte)i);
+ dtlsClient.send(data, 0, data.length);
+ }
+
+ byte[] buf = new byte[dtlsClient.getReceiveLimit()];
+ while (dtlsClient.receive(buf, 0, buf.length, 100) >= 0)
+ {
+ }
+
+ dtlsClient.close();
+
+ serverThread.shutdown();
+ }
+
+ static class ServerThread
+ extends Thread
+ {
+ private final DTLSServerProtocol serverProtocol;
+ private final DatagramTransport serverTransport;
+ private volatile boolean isShutdown = false;
+
+ ServerThread(DTLSServerProtocol serverProtocol, DatagramTransport serverTransport)
+ {
+ this.serverProtocol = serverProtocol;
+ this.serverTransport = serverTransport;
+ }
+
+ public void run()
+ {
+ try
+ {
+ MockDTLSServer server = new MockDTLSServer();
+ DTLSTransport dtlsServer = serverProtocol.accept(server, serverTransport);
+ byte[] buf = new byte[dtlsServer.getReceiveLimit()];
+ while (!isShutdown)
+ {
+ int length = dtlsServer.receive(buf, 0, buf.length, 1000);
+ if (length >= 0)
+ {
+ dtlsServer.send(buf, 0, length);
+ }
+ }
+ dtlsServer.close();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ void shutdown()
+ throws InterruptedException
+ {
+ if (!isShutdown)
+ {
+ isShutdown = true;
+ this.join();
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSServerTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSServerTest.java
new file mode 100644
index 00000000..06f57abb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSServerTest.java
@@ -0,0 +1,75 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.SocketTimeoutException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.DTLSServerProtocol;
+import org.bouncycastle.crypto.tls.DTLSTransport;
+import org.bouncycastle.crypto.tls.DatagramTransport;
+import org.bouncycastle.crypto.tls.UDPTransport;
+
+/**
+ * A simple test designed to conduct a DTLS handshake with an external DTLS client.
+ * <p>
+ * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+ * this package (under 'src/test/resources') for help configuring an external DTLS client.
+ * </p>
+ */
+public class DTLSServerTest
+{
+ public static void main(String[] args)
+ throws Exception
+ {
+ int port = 5556;
+
+ int mtu = 1500;
+
+ SecureRandom secureRandom = new SecureRandom();
+
+ DTLSServerProtocol serverProtocol = new DTLSServerProtocol(secureRandom);
+
+ byte[] data = new byte[mtu];
+ DatagramPacket packet = new DatagramPacket(data, mtu);
+
+ DatagramSocket socket = new DatagramSocket(port);
+ socket.receive(packet);
+
+ System.out.println("Accepting connection from " + packet.getAddress().getHostAddress() + ":" + port);
+ socket.connect(packet.getAddress(), packet.getPort());
+
+ /*
+ * NOTE: For simplicity, and since we don't yet have HelloVerifyRequest support, we just
+ * discard the initial packet, which the client should re-send anyway.
+ */
+
+ DatagramTransport transport = new UDPTransport(socket, mtu);
+
+ // Uncomment to see packets
+// transport = new LoggingDatagramTransport(transport, System.out);
+
+ MockDTLSServer server = new MockDTLSServer();
+ DTLSTransport dtlsServer = serverProtocol.accept(server, transport);
+
+ byte[] buf = new byte[dtlsServer.getReceiveLimit()];
+
+ while (!socket.isClosed())
+ {
+ try
+ {
+ int length = dtlsServer.receive(buf, 0, buf.length, 60000);
+ if (length >= 0)
+ {
+ System.out.write(buf, 0, length);
+ dtlsServer.send(buf, 0, length);
+ }
+ }
+ catch (SocketTimeoutException ste)
+ {
+ }
+ }
+
+ dtlsServer.close();
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestCase.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestCase.java
new file mode 100644
index 00000000..928647c1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestCase.java
@@ -0,0 +1,161 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+
+import org.bouncycastle.crypto.tls.DTLSClientProtocol;
+import org.bouncycastle.crypto.tls.DTLSServerProtocol;
+import org.bouncycastle.crypto.tls.DTLSTransport;
+import org.bouncycastle.crypto.tls.DatagramTransport;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.util.Arrays;
+
+public class DTLSTestCase extends TestCase
+{
+ private static void checkDTLSVersion(ProtocolVersion version)
+ {
+ if (version != null && !version.isDTLS())
+ {
+ throw new IllegalStateException("Non-DTLS version");
+ }
+ }
+
+ protected final TlsTestConfig config;
+
+ public DTLSTestCase(TlsTestConfig config, String name)
+ {
+ checkDTLSVersion(config.clientMinimumVersion);
+ checkDTLSVersion(config.clientOfferVersion);
+ checkDTLSVersion(config.serverMaximumVersion);
+ checkDTLSVersion(config.serverMinimumVersion);
+
+ this.config = config;
+
+ setName(name);
+ }
+
+ protected void runTest() throws Throwable
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ DTLSClientProtocol clientProtocol = new DTLSClientProtocol(secureRandom);
+ DTLSServerProtocol serverProtocol = new DTLSServerProtocol(secureRandom);
+
+ MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+ TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+ TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+ ServerThread serverThread = new ServerThread(serverProtocol, network.getServer(), serverImpl);
+ serverThread.start();
+
+ Exception caught = null;
+ try
+ {
+ DatagramTransport clientTransport = network.getClient();
+
+ if (TlsTestConfig.DEBUG)
+ {
+ clientTransport = new LoggingDatagramTransport(clientTransport, System.out);
+ }
+
+ DTLSTransport dtlsClient = clientProtocol.connect(clientImpl, clientTransport);
+
+ for (int i = 1; i <= 10; ++i)
+ {
+ byte[] data = new byte[i];
+ Arrays.fill(data, (byte)i);
+ dtlsClient.send(data, 0, data.length);
+ }
+
+ byte[] buf = new byte[dtlsClient.getReceiveLimit()];
+ while (dtlsClient.receive(buf, 0, buf.length, 100) >= 0)
+ {
+ }
+
+ dtlsClient.close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ logException(caught);
+ }
+
+ serverThread.shutdown();
+
+ // TODO Add checks that the various streams were closed
+
+ assertEquals("Client fatal alert connection end", config.expectFatalAlertConnectionEnd, clientImpl.firstFatalAlertConnectionEnd);
+ assertEquals("Server fatal alert connection end", config.expectFatalAlertConnectionEnd, serverImpl.firstFatalAlertConnectionEnd);
+
+ assertEquals("Client fatal alert description", config.expectFatalAlertDescription, clientImpl.firstFatalAlertDescription);
+ assertEquals("Server fatal alert description", config.expectFatalAlertDescription, serverImpl.firstFatalAlertDescription);
+
+ if (config.expectFatalAlertConnectionEnd == -1)
+ {
+ assertNull("Unexpected client exception", caught);
+ assertNull("Unexpected server exception", serverThread.caught);
+ }
+ }
+
+ protected void logException(Exception e)
+ {
+ if (TlsTestConfig.DEBUG)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ class ServerThread
+ extends Thread
+ {
+ private final DTLSServerProtocol serverProtocol;
+ private final DatagramTransport serverTransport;
+ private final TlsTestServerImpl serverImpl;
+
+ private volatile boolean isShutdown = false;
+ Exception caught = null;
+
+ ServerThread(DTLSServerProtocol serverProtocol, DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
+ {
+ this.serverProtocol = serverProtocol;
+ this.serverTransport = serverTransport;
+ this.serverImpl = serverImpl;
+ }
+
+ public void run()
+ {
+ try
+ {
+ DTLSTransport dtlsServer = serverProtocol.accept(serverImpl, serverTransport);
+ byte[] buf = new byte[dtlsServer.getReceiveLimit()];
+ while (!isShutdown)
+ {
+ int length = dtlsServer.receive(buf, 0, buf.length, 100);
+ if (length >= 0)
+ {
+ dtlsServer.send(buf, 0, length);
+ }
+ }
+ dtlsServer.close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ logException(caught);
+ }
+ }
+
+ void shutdown()
+ throws InterruptedException
+ {
+ if (!isShutdown)
+ {
+ isShutdown = true;
+ this.interrupt();
+ this.join();
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestSuite.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestSuite.java
new file mode 100644
index 00000000..df26bffa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestSuite.java
@@ -0,0 +1,126 @@
+package org.bouncycastle.crypto.tls.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+
+public class DTLSTestSuite extends TestSuite
+{
+ // Make the access to constants less verbose
+ static abstract class C extends TlsTestConfig {}
+
+ public static Test suite()
+ {
+ DTLSTestSuite testSuite = new DTLSTestSuite();
+
+ addFallbackTests(testSuite);
+ addVersionTests(testSuite, ProtocolVersion.DTLSv10);
+ addVersionTests(testSuite, ProtocolVersion.DTLSv12);
+
+ return testSuite;
+ }
+
+ private static void addFallbackTests(TestSuite testSuite)
+ {
+ {
+ TlsTestConfig c = createDTLSTestConfig(ProtocolVersion.DTLSv12);
+ c.clientFallback = true;
+
+ testSuite.addTest(new DTLSTestCase(c, "FallbackGood"));
+ }
+
+ /*
+ * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+ * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+ * alerts being raised
+ */
+
+// {
+// TlsTestConfig c = createDTLSTestConfig(ProtocolVersion.DTLSv12);
+// c.clientOfferVersion = ProtocolVersion.DTLSv10;
+// c.clientFallback = true;
+// c.expectServerFatalAlert(AlertDescription.inappropriate_fallback);
+//
+// testSuite.addTest(new DTLSTestCase(c, "FallbackBad"));
+// }
+
+ {
+ TlsTestConfig c = createDTLSTestConfig(ProtocolVersion.DTLSv12);
+ c.clientOfferVersion = ProtocolVersion.DTLSv10;
+
+ testSuite.addTest(new DTLSTestCase(c, "FallbackNone"));
+ }
+ }
+
+ private static void addVersionTests(TestSuite testSuite, ProtocolVersion version)
+ {
+ String prefix = version.toString().replaceAll("[ \\.]", "") + "_";
+
+ /*
+ * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+ * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+ * alerts being raised
+ */
+
+// {
+// TlsTestConfig c = createDTLSTestConfig(version);
+// c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+// c.expectServerFatalAlert(AlertDescription.decrypt_error);
+//
+// testSuite.addTest(new DTLSTestCase(c, prefix + "BadCertificateVerify"));
+// }
+//
+// {
+// TlsTestConfig c = createDTLSTestConfig(version);
+// c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+// c.expectServerFatalAlert(AlertDescription.bad_certificate);
+//
+// testSuite.addTest(new DTLSTestCase(c, prefix + "BadClientCertificate"));
+// }
+//
+// {
+// TlsTestConfig c = createDTLSTestConfig(version);
+// c.clientAuth = C.CLIENT_AUTH_NONE;
+// c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+// c.expectServerFatalAlert(AlertDescription.handshake_failure);
+//
+// testSuite.addTest(new DTLSTestCase(c, prefix + "BadMandatoryCertReqDeclined"));
+// }
+
+ {
+ TlsTestConfig c = createDTLSTestConfig(version);
+
+ testSuite.addTest(new DTLSTestCase(c, prefix + "GoodDefault"));
+ }
+
+ {
+ TlsTestConfig c = createDTLSTestConfig(version);
+ c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+ testSuite.addTest(new DTLSTestCase(c, prefix + "GoodNoCertReq"));
+ }
+
+ {
+ TlsTestConfig c = createDTLSTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+
+ testSuite.addTest(new DTLSTestCase(c, prefix + "GoodOptionalCertReqDeclined"));
+ }
+ }
+
+ private static TlsTestConfig createDTLSTestConfig(ProtocolVersion version)
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientMinimumVersion = ProtocolVersion.DTLSv10;
+ /*
+ * TODO We'd like to just set the offer version to DTLSv12, but there is a known issue with
+ * overly-restrictive version checks b/w BC DTLS 1.2 client, BC DTLS 1.0 server
+ */
+ c.clientOfferVersion = version;
+ c.serverMaximumVersion = version;
+ c.serverMinimumVersion = ProtocolVersion.DTLSv10;
+ return c;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/HTTPSServerThread.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/HTTPSServerThread.java
new file mode 100644
index 00000000..d55bc737
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/HTTPSServerThread.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.security.KeyStore;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManagerFactory;
+
+public class HTTPSServerThread
+ extends Thread
+{
+ private static final int PORT_NO = 8003;
+ private static final char[] SERVER_PASSWORD = "serverPassword".toCharArray();
+ private static final char[] TRUST_STORE_PASSWORD = "trustPassword".toCharArray();
+
+ /**
+ * Read a HTTP request
+ */
+ private void readRequest(
+ InputStream in)
+ throws IOException
+ {
+ int ch = 0;
+ int lastCh = 0;
+ while ((ch = in.read()) >= 0 && (ch != '\n' && lastCh != '\n'))
+ {
+ if (ch != '\r')
+ {
+ lastCh = ch;
+ }
+ }
+ }
+
+ /**
+ * Send a response
+ */
+ private void sendResponse(
+ OutputStream out)
+ {
+ PrintWriter pWrt = new PrintWriter(new OutputStreamWriter(out));
+ pWrt.print("HTTP/1.1 200 OK\r\n");
+ pWrt.print("Content-Type: text/html\r\n");
+ pWrt.print("\r\n");
+ pWrt.print("<html>\r\n");
+ pWrt.print("<body>\r\n");
+ pWrt.print("Hello World!\r\n");
+ pWrt.print("</body>\r\n");
+ pWrt.print("</html>\r\n");
+ pWrt.flush();
+ }
+
+ SSLContext createSSLContext()
+ throws Exception
+ {
+ KeyManagerFactory mgrFact = KeyManagerFactory.getInstance("SunX509");
+ KeyStore serverStore = KeyStore.getInstance("JKS");
+
+ serverStore.load(new ByteArrayInputStream(KeyStores.server), SERVER_PASSWORD);
+
+ mgrFact.init(serverStore, SERVER_PASSWORD);
+
+ // set up a trust manager so we can recognize the server
+ TrustManagerFactory trustFact = TrustManagerFactory.getInstance("SunX509");
+ KeyStore trustStore = KeyStore.getInstance("JKS");
+
+ trustStore.load(new ByteArrayInputStream(KeyStores.trustStore), TRUST_STORE_PASSWORD);
+
+ trustFact.init(trustStore);
+
+ // create a context and set up a socket factory
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+
+ sslContext.init(mgrFact.getKeyManagers(), trustFact.getTrustManagers(), null);
+
+ return sslContext;
+ }
+
+ public void run()
+ {
+ try
+ {
+ SSLContext sslContext = createSSLContext();
+ SSLServerSocketFactory fact = sslContext.getServerSocketFactory();
+
+ SSLServerSocket sSock = (SSLServerSocket)fact.createServerSocket(PORT_NO);
+ SSLSocket sslSock = (SSLSocket)sSock.accept();
+
+ sslSock.startHandshake();
+
+ readRequest(sslSock.getInputStream());
+
+ SSLSession session = sslSock.getSession();
+
+ sendResponse(sslSock.getOutputStream());
+
+ sslSock.close();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/KeyStores.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/KeyStores.java
new file mode 100644
index 00000000..bf71f965
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/KeyStores.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.crypto.tls.test;
+
+import org.bouncycastle.util.encoders.Base64;
+
+public class KeyStores
+{
+ static final byte[] trustStore = Base64.decode(
+ "/u3+7QAAAAIAAAABAAAAAgAGc2VydmVyAAABD34zDJEABVguNTA5AAABrzCC"
+ + "AaswggEUAgEBMA0GCSqGSIb3DQEBBQUAMB4xHDAaBgNVBAMTE1Rlc3QgQ0Eg"
+ + "Q2VydGlmaWNhdGUwHhcNMDYxMjEzMjM0MzI5WhcNMDYxMjIwMjM0MzI5WjAe"
+ + "MRwwGgYDVQQDExNUZXN0IENBIENlcnRpZmljYXRlMIGfMA0GCSqGSIb3DQEB"
+ + "AQUAA4GNADCBiQKBgQCmOumCM46ehsdcrZMw6tj0hMl5D0xf21gcyBj/ByIv"
+ + "pe008ukaN3zCXIUUUlAu0GkbI1sCbTD1V4qVZuaHqtfa/FINJjyZJy5w7KEx"
+ + "93OOF2/gyPlnEPGs6RPdThAPliSBsWKBKqtOdmKpwYn/NKuUNFRkFXVLLUPn"
+ + "jyEKjIcNywIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACi8J7raYqDFjXqAXXPJ"
+ + "ljZHqvzJNVaSWvBtK/oY7g0+sdGIjPR526fhSCxOZRr7G157ERqMnPjuFjcm"
+ + "cRlUOPsQtTl6KbUnxmKxa04UzDuNXzSx2oyGx5GCWx9u62hpO6vSpK69L9gH"
+ + "OUtM5dQXcoK4i3olScKaU8qaYb0mBAy8fnx5pen8B0bIg+pz47l5VxQ5NO8=");
+
+ static final byte[] server = Base64.decode(
+ "/u3+7QAAAAIAAAABAAAAAQAGc2VydmVyAAABD34zDJIAAAK8MIICuDAOBgor"
+ + "BgEEASoCEQEBBQAEggKk9OXWj3aBr6rV9Grcsm2YL+/2ShVsxbJVGMSWll1f"
+ + "U8z/mjhv5K/skgleTIMoyE5FzDDxJIGEmSMCkcHsnseXzxyhLpKBaz3N1Tk7"
+ + "KVPzXfrNh0FJwzw3lPWyC2ayT+ObQfAtzuI9SUWNLBzzpWeolUJ8gkXnLshX"
+ + "5RqmR735NRZdjgQOtNYBBErX/NOhTyi009/CZDNxgJQzywFmQcXBLhNSA+0i"
+ + "cE+LwZ4sZV0NXshPZiyNnsfqN/XNOyhpKr8a3VyVtee2nhO9+FY5IDEviHmR"
+ + "WDvH5TmB6Os3L6xvwE1ZQCPElk/cK2G1q9+zDBe4JDeo8o7lPhiNpVfcivnr"
+ + "+tyK9m7PqlssR0+ohuZGwrFMyzLugkOWh+qZ3pk8/7AwJosMvvTvIg36nN7R"
+ + "pdnouQNKqTm1Sr/UUnKyWqfG7zqKIuF72IkDwwafwuQ9YRX5PAOuRfo6bCyb"
+ + "D4w1OWdfGFciKMc1BrT+8JzhiTkzNe+jWEA5Tw1zeazPGpp1RHPz2np50G2s"
+ + "v025uiOSUilBe13Qpx0mAyx7z8Gl6eOxai6rQ/eB/Fu++BMx0OX8vO/K4//Q"
+ + "w43IPVPklGlb/3Qt7eoNf4tichuxrYJjHu4XbwnzB29Vmsan0XZFER7epvFj"
+ + "cAZCSG+O6G/f6Ib+7d7xYjLdUaPI60tJ4/C+AuYwZn4jJwTnMnMmwIypz4Z8"
+ + "XPXW5SbHlV5OkkFC+eAuuN4b2YXZmifeWv4fx65Ioir921LMJrorMizH41RJ"
+ + "/rAO5wGSwjeDJ784QhhLGbEUMygraRtdaz6MRx26InnoHY7V58ET3Z5YGV39"
+ + "p+9AtVhYw/QQmTZiF7hqnEMf9AMVFfac9qFF2+IN3g9HAMC6QLxJ1VuPn5oo"
+ + "lS//axPfjckO7iZx9x59/gAAAAEABVguNTA5AAABrzCCAaswggEUAgEBMA0G"
+ + "CSqGSIb3DQEBBQUAMB4xHDAaBgNVBAMTE1Rlc3QgQ0EgQ2VydGlmaWNhdGUw"
+ + "HhcNMDYxMjEzMjM0MzI5WhcNMDYxMjIwMjM0MzI5WjAeMRwwGgYDVQQDExNU"
+ + "ZXN0IENBIENlcnRpZmljYXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB"
+ + "gQCmOumCM46ehsdcrZMw6tj0hMl5D0xf21gcyBj/ByIvpe008ukaN3zCXIUU"
+ + "UlAu0GkbI1sCbTD1V4qVZuaHqtfa/FINJjyZJy5w7KEx93OOF2/gyPlnEPGs"
+ + "6RPdThAPliSBsWKBKqtOdmKpwYn/NKuUNFRkFXVLLUPnjyEKjIcNywIDAQAB"
+ + "MA0GCSqGSIb3DQEBBQUAA4GBACi8J7raYqDFjXqAXXPJljZHqvzJNVaSWvBt"
+ + "K/oY7g0+sdGIjPR526fhSCxOZRr7G157ERqMnPjuFjcmcRlUOPsQtTl6KbUn"
+ + "xmKxa04UzDuNXzSx2oyGx5GCWx9u62hpO6vSpK69L9gHOUtM5dQXcoK4i3ol"
+ + "ScKaU8qaYb0mBAy87VQaPfaiQqqxSgqifbSowlg+0fg=");
+
+ static final byte[] client = Base64.decode(
+ "MIACAQMwgAYJKoZIhvcNAQcBoIAkgASCA+gwgDCABgkqhkiG9w0BBwGggCSA"
+ + "BIIDDzCCAwswggMHBgsqhkiG9w0BDAoBAqCCArIwggKuMCgGCiqGSIb3DQEM"
+ + "AQMwGgQUyeN8U+ViAJCk1WGo8wTnRVOaE1oCAgQABIICgH3yMWB+dfMBNa07"
+ + "hn3smUSBTF+LUauM+kx+12nZt0QazBYg09yM+aVcTznettlDwE/PTpKZnrFT"
+ + "22DSQf5GwABwiL6KW+xyM8wV5vZx1xtqrQoNf2oNHnF0lB7ddSYM8jHqennb"
+ + "bzcVOdrrCqewqAfUU/CDugpwwI8c8Iy9ECri6vBMbfeIIZQTug+1952TCCiA"
+ + "E1bEDFGqgAoDsqYi6uNtpjmL/DPX/qmkbHAXUKxOtjTtxacfat2HgN97Gb7z"
+ + "+ZBNI5aaCSRoYwl+K9biWGID3EqsNmg0+2ELmFhE4mQZ78i7vNLTG7e5mj6v"
+ + "Qt+QuqN+daCbVqVTKxsFpODRRweobUTRxwEun6p0p3w3SUhR6p7ug1Jttgyu"
+ + "vfZug3PIk2j9fb26wAs2ZreVYut8hXpp/09ulCUudtlsYb/FD+H11v/JyopG"
+ + "2vYy0hL/gkMr41sGoVdyvzXeZUa7A1wYask+g7torad9sT8CPb3zXBd0kU2r"
+ + "lbSJIXQ8wTpheeUKe47YF9JVCF8WRc1dokD7fgfn6197pwyZpDEVrVyz1pUq"
+ + "jyum3ctONU4BSkAUVr9pTyeRFUeOE7SLuVu+c67PzNWXEc/HMVD4Hgs/idVc"
+ + "mrONk3xe385wFJK1iPe1kfRX5OXk9UWr2/yjgrahbkonWhJHnIlvs+b5rRKN"
+ + "qauwUZ+agMJV98Eyc2Lz0Lp/pNDRNjmGXeJLzAaAy46STov8sxwAgj3bQ7xu"
+ + "uEzdhJPDynAY5sWd3WnBY2ZhpawnToEKnD4u+eiH2MjUL2q/R2IPSmoyg9i2"
+ + "Ictgh4/ylrhm+lJDzcDrLDhC0m/t9EdhytH4erk/uCPcmAK/jpzFn6ltxcsx"
+ + "QjAbBgkqhkiG9w0BCRQxDh4MAGMAbABpAGUAbgB0MCMGCSqGSIb3DQEJFTEW"
+ + "BBS2wDxw7yH2OhKIgkDoQrTlavLvxQAAAAAAADCABgkqhkiG9w0BBwaggDCA"
+ + "AgEAMIAGCSqGSIb3DQEHATAoBgoqhkiG9w0BDAEGMBoEFORdyeTIG2oXDQBl"
+ + "I3aFNozDAkC2AgIEAKCABIIHICsc3D8rxLiDlegyXFfIejto66RW4u6b+d0C"
+ + "uAAz0G+dIvhQ9g3s++vUsX7x+icO/vjpgdo09aqtIg7T3suzfcHtU8CGSgtQ"
+ + "Tvml25LDC3IK3qI6cRqO+sCdvg18aS04IDKvDPYH4ca5btwPBIID6CStk/jY"
+ + "QFpnSI6VNz9IERi/nwuvuBYk+A0Z7+nQdhF+QkW1rzN+uz0dkNJt6ZbG5lEK"
+ + "GayV+FDTQcREMihYa716RN9sq3cm0jXXdttj998oS/QrpZDEcPqd4AM6EIL+"
+ + "PTA1tEQYxXa8msAPp+tLvXOtiD6v/4FO77EA7E5oR36a9en+M1QQasFU3VBf"
+ + "V9os92QlbtVUTkspJV9gXL6s4CGNptc7lUH+nIw65j9MOoyOU9w1qjPRlN/Z"
+ + "MTuhFooglE6TPd29Udwufqp+hHkW/7z5tBKkhGlZEClzD3IWhcF9NVraE/IV"
+ + "S5qVmx2Up7SeLZAXJ+AAznq5IXwE1dOUTkwYLcIrH1FuVA5rtOkB8Xt1LJq0"
+ + "ERkjJ5MfpxTxbKXp5PejzD98v54+s4INekcrI0jzz4pLsann7ex0r6CPsQsH"
+ + "+F3rBVaT3oHSKqoIm2Nw57oDjLLp5lP2qcCqps3y2dcVzu5NIzCSkVlxUaBK"
+ + "IT3xv0gvVJI7wnP0QM35MCywKkToJX5ajQKrDc4iCAAzmxaQzdBycxJPYByq"
+ + "VHvH7BJldJlMw5NHbTHlNoYKndMdAsHp7sUqERkDEGl80R86TlB4nJaDrfsx"
+ + "vL+KluiCY9AD7o+MEYZ9VYoNDUzVGH4pTr4wnv1UFoRWix5IZuDnnYkyijKD"
+ + "VtU5+mc0YtsPQIpKCBJOYt05bgpX5aPQ3s2lviYw3bvP0TrclYs/rx4wKVgW"
+ + "2GYLzPh0OVMBduCbzW1E58ieBsgRyQ7+2MTl9Nj+nznjCAfLSvrVEcwxVUQA"
+ + "ofcEbiECEJss2JNQq8erwgo8dP3atwQ1KeqMc8acICcOrI2rNxwVOLzPuCsl"
+ + "l8gwZOoxLZXuKMQbQu4as1HNS309dfWIWppvc3K4nDWg51HUCPbsIo3wm3rR"
+ + "igc/W3bf4Ppg0pLAS5c1s0Xau43u9GmIjGiDqYaasfcXnCKy2LNuUpbhoOC1"
+ + "o/wMC9gT45aJQ5vsVGe5XvfhLV7Y815EdI756s9PIEOnG8HbAtCAjOIVpQfP"
+ + "eF8+cnZyGGdsbmu7lYzg3whCpZ/L2RrTI86nEn5eePs4m2hDV0Oi9r6e2CIf"
+ + "nGUa0TB9jf1OMhOSBD+h4jx7b6uT+XLmG8qUxvkoItMDZLrJ0f8czO5MyOZa"
+ + "nEJoG42Fy2p7zD/qO72OUQISNmt8C1rQFsg9dBDib4DMu69vBsVrIV028sal"
+ + "+tq1UH3vFMbEYyVfPH3WDgxyHwUQK2qrz3WBdD/BMyuAV4DPI3SPTweROh8n"
+ + "yWJN1ppY9qybmTyHFCs6TRjPB9cVcdKSc0qYxgzblJiXXb3s9ANOb89aA3Ah"
+ + "anWnAhcEggLZN2yFCtrRuvJXvDQhj8qmye9UkjB2fEpWXV7H0natE3hSoB+C"
+ + "2krt3eBV0xJ1tLEco7T4zsbQQJRmeu/essOsfwpRfE5ZPoIeThf+UmWTsfsw"
+ + "r4fxeePMTy6iCnkF5/ro8k5WKnOsaV+HWy/ulwW2r/DMT3aDBWfHX5Hk8DpU"
+ + "+PlQhbyGTSTIJ/OFmbcWjMPt469o7+KrrF9IBrzJ42KzMw6xtKwtVb0232AM"
+ + "pIwnLzCHIiNO2qDBZAIDdF68+GU3RsEkHfR6d5myZVxnH4mhSCFFtHxgqTOz"
+ + "Ouo/uvgu72miZ0OFAy1zcMtfGMS3Md54MJkhSZq4ZgUo/EyJCeEDLGn8pMF1"
+ + "jX1WsUj96qMRyc+p2KnKs6ZL95BVa76SdIFz6ts6uoIem3drTXYJHNbw29OW"
+ + "Y0am+FmXTWFZFnin8Qu5Xct5l7FYIGA9VLOHL9Vtp+SXomcFEZpjMaxvtf4R"
+ + "xoAI2Bc9Ka+MNiz35O0JXhI9/t5uOdiN6ZOJlfpEWOK7ou5lDkZuDcrHvLvQ"
+ + "PocP2Yemd36xEgjFssR/tFITlhRBXHDeHwpvmXM4iiwD1wOGMqybXx/1g1m1"
+ + "0UBbKqDsgSLQC7c0TaRFJjj71T74PBMiapiQgijv9WINfmTgxNUukbK3Kqp/"
+ + "G3BmThoIDCB8WD1kxXBpaG62dSsih7yhPQJVEV3Y0tdkdChOk+Y3Wf8crV/Q"
+ + "P08pQH7H9yCi04S5fSJtIDtYARWhr1yl8EQMnu2X8J6r/DWcDmXUK4Bv1vqe"
+ + "tcnT5EAYFPeOMz21nonM3kJgUMNsxCQjKaEMEcUu8ZRnGUAFdG4lULPJn5NQ"
+ + "LTvWg8Y3GrHRLjpnd9k+8gWWzRIbBiwCwEbClMZffRd6kcA5ZoOhVngdyvvj"
+ + "BhkgadB5jC+ZRzExHxoEhzPJx3mxIVTqLuw7QxHz9OTcysvY/cGKCvmgzgk/"
+ + "TjTJsqcEAAAAAAAAAAAAAAAAAAAAAAAAMD0wITAJBgUrDgMCGgUABBQWfGA6"
+ + "lI+TXQiuXaa1V+LlHodhXAQUAwMIAAUvBYY+a7sXNlQeVEPAGkMCAgQAAAA=");
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/LoggingDatagramTransport.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/LoggingDatagramTransport.java
new file mode 100644
index 00000000..41816193
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/LoggingDatagramTransport.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.bouncycastle.crypto.tls.DatagramTransport;
+
+public class LoggingDatagramTransport
+ implements DatagramTransport
+{
+
+ private static final String HEX_CHARS = "0123456789ABCDEF";
+
+ private final DatagramTransport transport;
+ private final PrintStream output;
+ private final long launchTimestamp;
+
+ public LoggingDatagramTransport(DatagramTransport transport, PrintStream output)
+ {
+ this.transport = transport;
+ this.output = output;
+ this.launchTimestamp = System.currentTimeMillis();
+ }
+
+ public int getReceiveLimit()
+ throws IOException
+ {
+ return transport.getReceiveLimit();
+ }
+
+ public int getSendLimit()
+ throws IOException
+ {
+ return transport.getSendLimit();
+ }
+
+ public int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+ int length = transport.receive(buf, off, len, waitMillis);
+ if (length >= 0)
+ {
+ dumpDatagram("Received", buf, off, length);
+ }
+ return length;
+ }
+
+ public void send(byte[] buf, int off, int len)
+ throws IOException
+ {
+ dumpDatagram("Sending", buf, off, len);
+ transport.send(buf, off, len);
+ }
+
+ public void close()
+ throws IOException
+ {
+ }
+
+ private void dumpDatagram(String verb, byte[] buf, int off, int len)
+ throws IOException
+ {
+ long timestamp = System.currentTimeMillis() - launchTimestamp;
+ StringBuffer sb = new StringBuffer("(+" + timestamp + "ms) " + verb + " " + len + " byte datagram:");
+ for (int pos = 0; pos < len; ++pos)
+ {
+ if (pos % 16 == 0)
+ {
+ sb.append(System.getProperty("line.separator"));
+ sb.append(" ");
+ }
+ else if (pos % 16 == 8)
+ {
+ sb.append('-');
+ }
+ else
+ {
+ sb.append(' ');
+ }
+ int val = buf[off + pos] & 0xFF;
+ sb.append(HEX_CHARS.charAt(val >> 4));
+ sb.append(HEX_CHARS.charAt(val & 0xF));
+ }
+ dump(sb.toString());
+ }
+
+ private synchronized void dump(String s)
+ {
+ output.println(s);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSClient.java
new file mode 100644
index 00000000..c93b89ea
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSClient.java
@@ -0,0 +1,180 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CertificateRequest;
+import org.bouncycastle.crypto.tls.ClientCertificateType;
+import org.bouncycastle.crypto.tls.DefaultTlsClient;
+import org.bouncycastle.crypto.tls.MaxFragmentLength;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
+import org.bouncycastle.crypto.tls.TlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsCredentials;
+import org.bouncycastle.crypto.tls.TlsExtensionsUtils;
+import org.bouncycastle.crypto.tls.TlsSession;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+public class MockDTLSClient
+ extends DefaultTlsClient
+{
+ protected TlsSession session;
+
+ public MockDTLSClient(TlsSession session)
+ {
+ this.session = session;
+ }
+
+ public TlsSession getSessionToResume()
+ {
+ return this.session;
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("DTLS client raised alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println(message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("DTLS client received alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ }
+
+ public ProtocolVersion getClientVersion()
+ {
+ return ProtocolVersion.DTLSv12;
+ }
+
+ public ProtocolVersion getMinimumVersion()
+ {
+ return ProtocolVersion.DTLSv10;
+ }
+
+// public int[] getCipherSuites()
+// {
+// return Arrays.concatenate(super.getCipherSuites(),
+// new int[]
+// {
+// CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+// CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+// CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+// CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+// CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+// });
+// }
+
+ public Hashtable getClientExtensions() throws IOException
+ {
+ Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
+ TlsExtensionsUtils.addEncryptThenMACExtension(clientExtensions);
+ // TODO[draft-ietf-tls-session-hash-01] Enable once code-point assigned (only for compatible server though)
+// TlsExtensionsUtils.addExtendedMasterSecretExtension(clientExtensions);
+ TlsExtensionsUtils.addMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtils.addTruncatedHMacExtension(clientExtensions);
+ return clientExtensions;
+ }
+
+ public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException
+ {
+ super.notifyServerVersion(serverVersion);
+
+ System.out.println("Negotiated " + serverVersion);
+ }
+
+ public TlsAuthentication getAuthentication()
+ throws IOException
+ {
+ return new TlsAuthentication()
+ {
+ public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate)
+ throws IOException
+ {
+ Certificate[] chain = serverCertificate.getCertificateList();
+ System.out.println("Received server certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+
+ public TlsCredentials getClientCredentials(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ short[] certificateTypes = certificateRequest.getCertificateTypes();
+ if (certificateTypes == null || !Arrays.contains(certificateTypes, ClientCertificateType.rsa_sign))
+ {
+ return null;
+ }
+
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ Vector sigAlgs = certificateRequest.getSupportedSignatureAlgorithms();
+ if (sigAlgs != null)
+ {
+ for (int i = 0; i < sigAlgs.size(); ++i)
+ {
+ SignatureAndHashAlgorithm sigAlg = (SignatureAndHashAlgorithm)
+ sigAlgs.elementAt(i);
+ if (sigAlg.getSignature() == SignatureAlgorithm.rsa)
+ {
+ signatureAndHashAlgorithm = sigAlg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+
+ return TlsTestUtils.loadSignerCredentials(context, new String[] { "x509-client.pem", "x509-ca.pem" },
+ "x509-client-key.pem", signatureAndHashAlgorithm);
+ }
+ };
+ }
+
+ public void notifyHandshakeComplete() throws IOException
+ {
+ super.notifyHandshakeComplete();
+
+ TlsSession newSession = context.getResumableSession();
+ if (newSession != null)
+ {
+ byte[] newSessionID = newSession.getSessionID();
+ String hex = Hex.toHexString(newSessionID);
+
+ if (this.session != null && Arrays.areEqual(this.session.getSessionID(), newSessionID))
+ {
+ System.out.println("Resumed session: " + hex);
+ }
+ else
+ {
+ System.out.println("Established session: " + hex);
+ }
+
+ this.session = newSession;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSServer.java
new file mode 100644
index 00000000..bdf533f8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDTLSServer.java
@@ -0,0 +1,138 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CertificateRequest;
+import org.bouncycastle.crypto.tls.CipherSuite;
+import org.bouncycastle.crypto.tls.ClientCertificateType;
+import org.bouncycastle.crypto.tls.DefaultTlsServer;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
+import org.bouncycastle.crypto.tls.TlsEncryptionCredentials;
+import org.bouncycastle.crypto.tls.TlsSignerCredentials;
+import org.bouncycastle.crypto.tls.TlsUtils;
+import org.bouncycastle.util.Arrays;
+
+public class MockDTLSServer
+ extends DefaultTlsServer
+{
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("DTLS server raised alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println(message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("DTLS server received alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ }
+
+ protected int[] getCipherSuites()
+ {
+ return Arrays.concatenate(super.getCipherSuites(),
+ new int[]
+ {
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+ CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+ CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+ CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+ });
+ }
+
+ public CertificateRequest getCertificateRequest() throws IOException
+ {
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ Vector serverSigAlgs = null;
+ if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(serverVersion))
+ {
+ serverSigAlgs = TlsUtils.getDefaultSupportedSignatureAlgorithms();
+ }
+
+ Vector certificateAuthorities = new Vector();
+ certificateAuthorities.add(TlsTestUtils.loadCertificateResource("x509-ca.pem").getSubject());
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+
+ public void notifyClientCertificate(org.bouncycastle.crypto.tls.Certificate clientCertificate)
+ throws IOException
+ {
+ Certificate[] chain = clientCertificate.getCertificateList();
+ System.out.println("Received client certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " (" + entry.getSubject()
+ + ")");
+ }
+ }
+
+ protected ProtocolVersion getMaximumVersion()
+ {
+ return ProtocolVersion.DTLSv12;
+ }
+
+ protected ProtocolVersion getMinimumVersion()
+ {
+ return ProtocolVersion.DTLSv10;
+ }
+
+ protected TlsEncryptionCredentials getRSAEncryptionCredentials()
+ throws IOException
+ {
+ return TlsTestUtils.loadEncryptionCredentials(context, new String[]{"x509-server.pem", "x509-ca.pem"},
+ "x509-server-key.pem");
+ }
+
+ protected TlsSignerCredentials getRSASignerCredentials()
+ throws IOException
+ {
+ /*
+ * TODO Note that this code fails to provide default value for the client supported
+ * algorithms if it wasn't sent.
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ Vector sigAlgs = supportedSignatureAlgorithms;
+ if (sigAlgs != null)
+ {
+ for (int i = 0; i < sigAlgs.size(); ++i)
+ {
+ SignatureAndHashAlgorithm sigAlg = (SignatureAndHashAlgorithm)
+ sigAlgs.elementAt(i);
+ if (sigAlg.getSignature() == SignatureAlgorithm.rsa)
+ {
+ signatureAndHashAlgorithm = sigAlg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+ return TlsTestUtils.loadSignerCredentials(context, new String[]{"x509-server.pem", "x509-ca.pem"},
+ "x509-server-key.pem", signatureAndHashAlgorithm);
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDatagramAssociation.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDatagramAssociation.java
new file mode 100644
index 00000000..b8b7c069
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockDatagramAssociation.java
@@ -0,0 +1,110 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.tls.DatagramTransport;
+
+public class MockDatagramAssociation
+{
+ private int mtu;
+ private MockDatagramTransport client, server;
+
+ public MockDatagramAssociation(int mtu)
+ {
+ this.mtu = mtu;
+
+ Vector clientQueue = new Vector();
+ Vector serverQueue = new Vector();
+
+ this.client = new MockDatagramTransport(clientQueue, serverQueue);
+ this.server = new MockDatagramTransport(serverQueue, clientQueue);
+ }
+
+ public DatagramTransport getClient()
+ {
+ return client;
+ }
+
+ public DatagramTransport getServer()
+ {
+ return server;
+ }
+
+ private class MockDatagramTransport
+ implements DatagramTransport
+ {
+ private Vector receiveQueue, sendQueue;
+
+ MockDatagramTransport(Vector receiveQueue, Vector sendQueue)
+ {
+ this.receiveQueue = receiveQueue;
+ this.sendQueue = sendQueue;
+ }
+
+ public int getReceiveLimit()
+ throws IOException
+ {
+ return mtu;
+ }
+
+ public int getSendLimit()
+ throws IOException
+ {
+ return mtu;
+ }
+
+ public int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+ synchronized (receiveQueue)
+ {
+ if (receiveQueue.isEmpty())
+ {
+ try
+ {
+ receiveQueue.wait(waitMillis);
+ }
+ catch (InterruptedException e)
+ {
+ // TODO Keep waiting until full wait expired?
+ }
+ if (receiveQueue.isEmpty())
+ {
+ return -1;
+ }
+ }
+ DatagramPacket packet = (DatagramPacket)receiveQueue.remove(0);
+ int copyLength = Math.min(len, packet.getLength());
+ System.arraycopy(packet.getData(), packet.getOffset(), buf, off, copyLength);
+ return copyLength;
+ }
+ }
+
+ public void send(byte[] buf, int off, int len)
+ throws IOException
+ {
+ if (len > mtu)
+ {
+ // TODO Simulate rejection?
+ }
+
+ byte[] copy = new byte[len];
+ System.arraycopy(buf, off, copy, 0, len);
+ DatagramPacket packet = new DatagramPacket(copy, len);
+
+ synchronized (sendQueue)
+ {
+ sendQueue.addElement(packet);
+ sendQueue.notify();
+ }
+ }
+
+ public void close()
+ throws IOException
+ {
+ // TODO?
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockPSKTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockPSKTlsClient.java
new file mode 100644
index 00000000..7af59572
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockPSKTlsClient.java
@@ -0,0 +1,134 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.BasicTlsPSKIdentity;
+import org.bouncycastle.crypto.tls.CipherSuite;
+import org.bouncycastle.crypto.tls.PSKTlsClient;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.ServerOnlyTlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsExtensionsUtils;
+import org.bouncycastle.crypto.tls.TlsPSKIdentity;
+import org.bouncycastle.crypto.tls.TlsSession;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+class MockPSKTlsClient
+ extends PSKTlsClient
+{
+ TlsSession session;
+
+ MockPSKTlsClient(TlsSession session)
+ {
+ this(session, new BasicTlsPSKIdentity("client", new byte[16]));
+ }
+
+ MockPSKTlsClient(TlsSession session, TlsPSKIdentity pskIdentity)
+ {
+ super(pskIdentity);
+
+ this.session = session;
+ }
+
+ public TlsSession getSessionToResume()
+ {
+ return this.session;
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS-PSK client raised alert: " + AlertLevel.getText(alertLevel) + ", "
+ + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS-PSK client received alert: " + AlertLevel.getText(alertLevel) + ", "
+ + AlertDescription.getText(alertDescription));
+ }
+
+ public void notifyHandshakeComplete() throws IOException
+ {
+ super.notifyHandshakeComplete();
+
+ TlsSession newSession = context.getResumableSession();
+ if (newSession != null)
+ {
+ byte[] newSessionID = newSession.getSessionID();
+ String hex = Hex.toHexString(newSessionID);
+
+ if (this.session != null && Arrays.areEqual(this.session.getSessionID(), newSessionID))
+ {
+ System.out.println("Resumed session: " + hex);
+ }
+ else
+ {
+ System.out.println("Established session: " + hex);
+ }
+
+ this.session = newSession;
+ }
+ }
+
+ public int[] getCipherSuites()
+ {
+ return new int[]{ CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA };
+ }
+
+ public ProtocolVersion getMinimumVersion()
+ {
+ return ProtocolVersion.TLSv12;
+ }
+
+ public Hashtable getClientExtensions() throws IOException
+ {
+ Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
+ TlsExtensionsUtils.addEncryptThenMACExtension(clientExtensions);
+ return clientExtensions;
+ }
+
+ public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException
+ {
+ super.notifyServerVersion(serverVersion);
+
+ System.out.println("TLS-PSK client negotiated " + serverVersion);
+ }
+
+ public TlsAuthentication getAuthentication() throws IOException
+ {
+ return new ServerOnlyTlsAuthentication()
+ {
+ public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate)
+ throws IOException
+ {
+ Certificate[] chain = serverCertificate.getCertificateList();
+ System.out.println("TLS-PSK client received server certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+ };
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockPSKTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockPSKTlsServer.java
new file mode 100644
index 00000000..d5ca7ab7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockPSKTlsServer.java
@@ -0,0 +1,110 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CipherSuite;
+import org.bouncycastle.crypto.tls.PSKTlsServer;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.TlsEncryptionCredentials;
+import org.bouncycastle.crypto.tls.TlsPSKIdentityManager;
+import org.bouncycastle.util.Strings;
+
+class MockPSKTlsServer
+ extends PSKTlsServer
+{
+ MockPSKTlsServer()
+ {
+ super(new MyIdentityManager());
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS-PSK server raised alert: " + AlertLevel.getText(alertLevel) + ", "
+ + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS-PSK server received alert: " + AlertLevel.getText(alertLevel) + ", "
+ + AlertDescription.getText(alertDescription));
+ }
+
+ public void notifyHandshakeComplete() throws IOException
+ {
+ super.notifyHandshakeComplete();
+
+ byte[] pskIdentity = context.getSecurityParameters().getPSKIdentity();
+ if (pskIdentity != null)
+ {
+ String name = Strings.fromUTF8ByteArray(pskIdentity);
+ System.out.println("TLS-PSK server completed handshake for PSK identity: " + name);
+ }
+ }
+
+ protected int[] getCipherSuites()
+ {
+ return new int[]{ CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA };
+ }
+
+ protected ProtocolVersion getMaximumVersion()
+ {
+ return ProtocolVersion.TLSv12;
+ }
+
+ protected ProtocolVersion getMinimumVersion()
+ {
+ return ProtocolVersion.TLSv12;
+ }
+
+ public ProtocolVersion getServerVersion() throws IOException
+ {
+ ProtocolVersion serverVersion = super.getServerVersion();
+
+ System.out.println("TLS-PSK server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ protected TlsEncryptionCredentials getRSAEncryptionCredentials() throws IOException
+ {
+ return TlsTestUtils.loadEncryptionCredentials(context, new String[]{ "x509-server.pem", "x509-ca.pem" },
+ "x509-server-key.pem");
+ }
+
+ static class MyIdentityManager
+ implements TlsPSKIdentityManager
+ {
+ public byte[] getHint()
+ {
+ return Strings.toUTF8ByteArray("hint");
+ }
+
+ public byte[] getPSK(byte[] identity)
+ {
+ if (identity != null)
+ {
+ String name = Strings.fromUTF8ByteArray(identity);
+ if (name.equals("client"))
+ {
+ return new byte[16];
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockSRPTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockSRPTlsClient.java
new file mode 100644
index 00000000..e6f20d60
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockSRPTlsClient.java
@@ -0,0 +1,120 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Hashtable;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CipherSuite;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SRPTlsClient;
+import org.bouncycastle.crypto.tls.ServerOnlyTlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsExtensionsUtils;
+import org.bouncycastle.crypto.tls.TlsSession;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+class MockSRPTlsClient
+ extends SRPTlsClient
+{
+ TlsSession session;
+
+ MockSRPTlsClient(TlsSession session, byte[] identity, byte[] password)
+ {
+ super(identity, password);
+
+ this.session = session;
+ }
+
+ public TlsSession getSessionToResume()
+ {
+ return this.session;
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS-SRP client raised alert: " + AlertLevel.getText(alertLevel) + ", "
+ + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS-SRP client received alert: " + AlertLevel.getText(alertLevel) + ", "
+ + AlertDescription.getText(alertDescription));
+ }
+
+ public void notifyHandshakeComplete() throws IOException
+ {
+ super.notifyHandshakeComplete();
+
+ TlsSession newSession = context.getResumableSession();
+ if (newSession != null)
+ {
+ byte[] newSessionID = newSession.getSessionID();
+ String hex = Hex.toHexString(newSessionID);
+
+ if (this.session != null && Arrays.areEqual(this.session.getSessionID(), newSessionID))
+ {
+ System.out.println("Resumed session: " + hex);
+ }
+ else
+ {
+ System.out.println("Established session: " + hex);
+ }
+
+ this.session = newSession;
+ }
+ }
+
+ public ProtocolVersion getMinimumVersion()
+ {
+ return ProtocolVersion.TLSv12;
+ }
+
+ public Hashtable getClientExtensions() throws IOException
+ {
+ Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
+ TlsExtensionsUtils.addEncryptThenMACExtension(clientExtensions);
+ return clientExtensions;
+ }
+
+ public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException
+ {
+ super.notifyServerVersion(serverVersion);
+
+ System.out.println("TLS-SRP client negotiated " + serverVersion);
+ }
+
+ public TlsAuthentication getAuthentication() throws IOException
+ {
+ return new ServerOnlyTlsAuthentication()
+ {
+ public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate)
+ throws IOException
+ {
+ Certificate[] chain = serverCertificate.getCertificateList();
+ System.out.println("TLS-SRP client received server certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+ };
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockSRPTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockSRPTlsServer.java
new file mode 100644
index 00000000..35937572
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockSRPTlsServer.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.agreement.srp.SRP6StandardGroups;
+import org.bouncycastle.crypto.agreement.srp.SRP6VerifierGenerator;
+import org.bouncycastle.crypto.params.SRP6GroupParameters;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.HashAlgorithm;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SRPTlsServer;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SimulatedTlsSRPIdentityManager;
+import org.bouncycastle.crypto.tls.TlsSRPIdentityManager;
+import org.bouncycastle.crypto.tls.TlsSRPLoginParameters;
+import org.bouncycastle.crypto.tls.TlsSignerCredentials;
+import org.bouncycastle.crypto.tls.TlsUtils;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Strings;
+
+class MockSRPTlsServer
+ extends SRPTlsServer
+{
+ static final SRP6GroupParameters TEST_GROUP = SRP6StandardGroups.rfc5054_1024;
+ static final byte[] TEST_IDENTITY = Strings.toUTF8ByteArray("client");
+ static final byte[] TEST_PASSWORD = Strings.toUTF8ByteArray("password");
+ static final byte[] TEST_SALT = Strings.toUTF8ByteArray("salt");
+ static final byte[] TEST_SEED_KEY = Strings.toUTF8ByteArray("seed_key");
+
+ MockSRPTlsServer()
+ {
+ super(new MyIdentityManager());
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS-SRP server raised alert: " + AlertLevel.getText(alertLevel) + ", "
+ + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS-SRP server received alert: " + AlertLevel.getText(alertLevel) + ", "
+ + AlertDescription.getText(alertDescription));
+ }
+
+ public void notifyHandshakeComplete() throws IOException
+ {
+ super.notifyHandshakeComplete();
+
+ byte[] srpIdentity = context.getSecurityParameters().getSRPIdentity();
+ if (srpIdentity != null)
+ {
+ String name = Strings.fromUTF8ByteArray(srpIdentity);
+ System.out.println("TLS-SRP server completed handshake for SRP identity: " + name);
+ }
+ }
+
+ protected ProtocolVersion getMaximumVersion()
+ {
+ return ProtocolVersion.TLSv12;
+ }
+
+ protected ProtocolVersion getMinimumVersion()
+ {
+ return ProtocolVersion.TLSv12;
+ }
+
+ public ProtocolVersion getServerVersion() throws IOException
+ {
+ ProtocolVersion serverVersion = super.getServerVersion();
+
+ System.out.println("TLS-SRP server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ protected TlsSignerCredentials getDSASignerCredentials() throws IOException
+ {
+ return TlsTestUtils.loadSignerCredentials(context, supportedSignatureAlgorithms, SignatureAlgorithm.dsa,
+ "x509-server-dsa.pem", "x509-server-key-dsa.pem");
+ }
+
+ protected TlsSignerCredentials getRSASignerCredentials() throws IOException
+ {
+ return TlsTestUtils.loadSignerCredentials(context, supportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+ "x509-server.pem", "x509-server-key.pem");
+ }
+
+ static class MyIdentityManager
+ implements TlsSRPIdentityManager
+ {
+ protected SimulatedTlsSRPIdentityManager unknownIdentityManager = SimulatedTlsSRPIdentityManager.getRFC5054Default(
+ TEST_GROUP, TEST_SEED_KEY);
+
+ public TlsSRPLoginParameters getLoginParameters(byte[] identity)
+ {
+ if (Arrays.areEqual(TEST_IDENTITY, identity))
+ {
+ SRP6VerifierGenerator verifierGenerator = new SRP6VerifierGenerator();
+ verifierGenerator.init(TEST_GROUP, TlsUtils.createHash(HashAlgorithm.sha1));
+
+ BigInteger verifier = verifierGenerator.generateVerifier(TEST_SALT, identity, TEST_PASSWORD);
+
+ return new TlsSRPLoginParameters(TEST_GROUP, verifier, TEST_SALT);
+ }
+
+ return unknownIdentityManager.getLoginParameters(identity);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsClient.java
new file mode 100644
index 00000000..3530b4a7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsClient.java
@@ -0,0 +1,170 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CertificateRequest;
+import org.bouncycastle.crypto.tls.ClientCertificateType;
+import org.bouncycastle.crypto.tls.DefaultTlsClient;
+import org.bouncycastle.crypto.tls.MaxFragmentLength;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
+import org.bouncycastle.crypto.tls.TlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsCredentials;
+import org.bouncycastle.crypto.tls.TlsExtensionsUtils;
+import org.bouncycastle.crypto.tls.TlsSession;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+
+class MockTlsClient
+ extends DefaultTlsClient
+{
+ TlsSession session;
+
+ MockTlsClient(TlsSession session)
+ {
+ this.session = session;
+ }
+
+ public TlsSession getSessionToResume()
+ {
+ return this.session;
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client raised alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client received alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ }
+
+// public int[] getCipherSuites()
+// {
+// return Arrays.concatenate(super.getCipherSuites(),
+// new int[]
+// {
+// CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+// CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+// CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+// CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+// CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+// });
+// }
+
+ public Hashtable getClientExtensions() throws IOException
+ {
+ Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
+ TlsExtensionsUtils.addEncryptThenMACExtension(clientExtensions);
+ // TODO[draft-ietf-tls-session-hash-01] Enable once code-point assigned (only for compatible server though)
+// TlsExtensionsUtils.addExtendedMasterSecretExtension(clientExtensions);
+ TlsExtensionsUtils.addMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtils.addTruncatedHMacExtension(clientExtensions);
+ return clientExtensions;
+ }
+
+ public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException
+ {
+ super.notifyServerVersion(serverVersion);
+
+ System.out.println("TLS client negotiated " + serverVersion);
+ }
+
+ public TlsAuthentication getAuthentication()
+ throws IOException
+ {
+ return new TlsAuthentication()
+ {
+ public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate)
+ throws IOException
+ {
+ Certificate[] chain = serverCertificate.getCertificateList();
+ System.out.println("TLS client received server certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+
+ public TlsCredentials getClientCredentials(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ short[] certificateTypes = certificateRequest.getCertificateTypes();
+ if (certificateTypes == null || !Arrays.contains(certificateTypes, ClientCertificateType.rsa_sign))
+ {
+ return null;
+ }
+
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ Vector sigAlgs = certificateRequest.getSupportedSignatureAlgorithms();
+ if (sigAlgs != null)
+ {
+ for (int i = 0; i < sigAlgs.size(); ++i)
+ {
+ SignatureAndHashAlgorithm sigAlg = (SignatureAndHashAlgorithm)
+ sigAlgs.elementAt(i);
+ if (sigAlg.getSignature() == SignatureAlgorithm.rsa)
+ {
+ signatureAndHashAlgorithm = sigAlg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+
+ return TlsTestUtils.loadSignerCredentials(context, new String[] { "x509-client.pem", "x509-ca.pem" },
+ "x509-client-key.pem", signatureAndHashAlgorithm);
+ }
+ };
+ }
+
+ public void notifyHandshakeComplete() throws IOException
+ {
+ super.notifyHandshakeComplete();
+
+ TlsSession newSession = context.getResumableSession();
+ if (newSession != null)
+ {
+ byte[] newSessionID = newSession.getSessionID();
+ String hex = Hex.toHexString(newSessionID);
+
+ if (this.session != null && Arrays.areEqual(this.session.getSessionID(), newSessionID))
+ {
+ System.out.println("Resumed session: " + hex);
+ }
+ else
+ {
+ System.out.println("Established session: " + hex);
+ }
+
+ this.session = newSession;
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsServer.java
new file mode 100644
index 00000000..3aa47e85
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/MockTlsServer.java
@@ -0,0 +1,143 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CertificateRequest;
+import org.bouncycastle.crypto.tls.CipherSuite;
+import org.bouncycastle.crypto.tls.ClientCertificateType;
+import org.bouncycastle.crypto.tls.DefaultTlsServer;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
+import org.bouncycastle.crypto.tls.TlsEncryptionCredentials;
+import org.bouncycastle.crypto.tls.TlsSignerCredentials;
+import org.bouncycastle.crypto.tls.TlsUtils;
+import org.bouncycastle.util.Arrays;
+
+class MockTlsServer
+ extends DefaultTlsServer
+{
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS server raised alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS server received alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ }
+
+ protected int[] getCipherSuites()
+ {
+ return Arrays.concatenate(super.getCipherSuites(),
+ new int[]
+ {
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+ CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+ CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+ CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+ });
+ }
+
+ protected ProtocolVersion getMaximumVersion()
+ {
+ return ProtocolVersion.TLSv12;
+ }
+
+ public ProtocolVersion getServerVersion() throws IOException
+ {
+ ProtocolVersion serverVersion = super.getServerVersion();
+
+ System.out.println("TLS server negotiated " + serverVersion);
+
+ return serverVersion;
+ }
+
+ public CertificateRequest getCertificateRequest() throws IOException
+ {
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ Vector serverSigAlgs = null;
+ if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(serverVersion))
+ {
+ serverSigAlgs = TlsUtils.getDefaultSupportedSignatureAlgorithms();
+ }
+
+ Vector certificateAuthorities = new Vector();
+ certificateAuthorities.add(TlsTestUtils.loadCertificateResource("x509-ca.pem").getSubject());
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+
+ public void notifyClientCertificate(org.bouncycastle.crypto.tls.Certificate clientCertificate)
+ throws IOException
+ {
+ Certificate[] chain = clientCertificate.getCertificateList();
+ System.out.println("TLS server received client certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+
+ protected TlsEncryptionCredentials getRSAEncryptionCredentials()
+ throws IOException
+ {
+ return TlsTestUtils.loadEncryptionCredentials(context, new String[]{"x509-server.pem", "x509-ca.pem"},
+ "x509-server-key.pem");
+ }
+
+ protected TlsSignerCredentials getRSASignerCredentials()
+ throws IOException
+ {
+ /*
+ * TODO Note that this code fails to provide default value for the client supported
+ * algorithms if it wasn't sent.
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ Vector sigAlgs = supportedSignatureAlgorithms;
+ if (sigAlgs != null)
+ {
+ for (int i = 0; i < sigAlgs.size(); ++i)
+ {
+ SignatureAndHashAlgorithm sigAlg = (SignatureAndHashAlgorithm)
+ sigAlgs.elementAt(i);
+ if (sigAlg.getSignature() == SignatureAlgorithm.rsa)
+ {
+ signatureAndHashAlgorithm = sigAlg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+
+ return TlsTestUtils.loadSignerCredentials(context, new String[]{"x509-server.pem", "x509-ca.pem"},
+ "x509-server-key.pem", signatureAndHashAlgorithm);
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/NetworkInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/NetworkInputStream.java
new file mode 100644
index 00000000..e5f47706
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/NetworkInputStream.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Tracks and enforces close() calls, without closing the underlying InputStream
+ */
+class NetworkInputStream extends FilterInputStream
+{
+ boolean closed = false;
+
+ public NetworkInputStream(InputStream input)
+ {
+ super(input);
+ }
+
+ synchronized boolean isClosed()
+ {
+ return closed;
+ }
+
+ public int available() throws IOException
+ {
+ checkNotClosed();
+ return in.available();
+ }
+
+ public synchronized void close() throws IOException
+ {
+ closed = true;
+ }
+
+ public int read() throws IOException
+ {
+ checkNotClosed();
+ return in.read();
+ }
+
+ public int read(byte[] b) throws IOException
+ {
+ checkNotClosed();
+ return in.read(b);
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ checkNotClosed();
+ return in.read(b, off, len);
+ }
+
+ protected synchronized void checkNotClosed() throws IOException
+ {
+ if (closed)
+ {
+ throw new IOException("NetworkInputStream closed");
+ }
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/NetworkOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/NetworkOutputStream.java
new file mode 100644
index 00000000..a11694b7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/NetworkOutputStream.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Tracks and enforces close() calls, without closing the underlying OutputStream
+ */
+class NetworkOutputStream extends FilterOutputStream
+{
+ boolean closed = false;
+
+ public NetworkOutputStream(OutputStream output)
+ {
+ super(output);
+ }
+
+ synchronized boolean isClosed()
+ {
+ return closed;
+ }
+
+ public synchronized void close() throws IOException
+ {
+ closed = true;
+ }
+
+ public void write(int b) throws IOException
+ {
+ checkNotClosed();
+ out.write(b);
+ }
+
+ public void write(byte[] b) throws IOException
+ {
+ checkNotClosed();
+ out.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ checkNotClosed();
+ out.write(b, off, len);
+ }
+
+ protected synchronized void checkNotClosed() throws IOException
+ {
+ if (closed)
+ {
+ throw new IOException("NetworkOutputStream closed");
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/PSKTlsClientTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/PSKTlsClientTest.java
new file mode 100644
index 00000000..4b152f4f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/PSKTlsClientTest.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.BasicTlsPSKIdentity;
+import org.bouncycastle.crypto.tls.TlsClient;
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+
+/**
+ * A simple test designed to conduct a TLS handshake with an external TLS server.
+ * <p>
+ * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+ * this package (under 'src/test/resources') for help configuring an external TLS server.
+ * </p><p>
+ * In both cases, extra options are required to enable PSK ciphersuites and configure identities/keys.
+ * </p>
+ */
+public class PSKTlsClientTest
+{
+ private static final SecureRandom secureRandom = new SecureRandom();
+
+ public static void main(String[] args) throws Exception
+ {
+ InetAddress address = InetAddress.getLocalHost();
+ int port = 5556;
+
+ long time1 = System.currentTimeMillis();
+
+ /*
+ * Note: This is the default PSK identity for 'openssl s_server' testing, the server must be
+ * started with "-psk 6161616161" to make the keys match, and possibly the "-psk_hint"
+ * option should be present.
+ */
+ String psk_identity = "Client_identity";
+ byte[] psk = new byte[]{ 0x61, 0x61, 0x61, 0x61, 0x61 };
+
+ BasicTlsPSKIdentity pskIdentity = new BasicTlsPSKIdentity(psk_identity, psk);
+
+ MockPSKTlsClient client = new MockPSKTlsClient(null, pskIdentity);
+ TlsClientProtocol protocol = openTlsConnection(address, port, client);
+ protocol.close();
+
+ long time2 = System.currentTimeMillis();
+ System.out.println("Elapsed 1: " + (time2 - time1) + "ms");
+
+ client = new MockPSKTlsClient(client.getSessionToResume(), pskIdentity);
+ protocol = openTlsConnection(address, port, client);
+
+ long time3 = System.currentTimeMillis();
+ System.out.println("Elapsed 2: " + (time3 - time2) + "ms");
+
+ OutputStream output = protocol.getOutputStream();
+ output.write("GET / HTTP/1.1\r\n\r\n".getBytes("UTF-8"));
+ output.flush();
+
+ InputStream input = protocol.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(input));
+
+ String line;
+ while ((line = reader.readLine()) != null)
+ {
+ System.out.println(">>> " + line);
+ }
+
+ protocol.close();
+ }
+
+ static TlsClientProtocol openTlsConnection(InetAddress address, int port, TlsClient client) throws IOException
+ {
+ Socket s = new Socket(address, port);
+ TlsClientProtocol protocol = new TlsClientProtocol(s.getInputStream(), s.getOutputStream(), secureRandom);
+ protocol.connect(client);
+ return protocol;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsClientTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsClientTest.java
new file mode 100644
index 00000000..bf289d88
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsClientTest.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.TlsClient;
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+
+/**
+ * A simple test designed to conduct a TLS handshake with an external TLS server.
+ * <p>
+ * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+ * this package (under 'src/test/resources') for help configuring an external TLS server.
+ * </p>
+ */
+public class TlsClientTest
+{
+ private static final SecureRandom secureRandom = new SecureRandom();
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ InetAddress address = InetAddress.getLocalHost();
+ int port = 5556;
+
+ long time1 = System.currentTimeMillis();
+
+ MockTlsClient client = new MockTlsClient(null);
+ TlsClientProtocol protocol = openTlsConnection(address, port, client);
+ protocol.close();
+
+ long time2 = System.currentTimeMillis();
+ System.out.println("Elapsed 1: " + (time2 - time1) + "ms");
+
+ client = new MockTlsClient(client.getSessionToResume());
+ protocol = openTlsConnection(address, port, client);
+
+ long time3 = System.currentTimeMillis();
+ System.out.println("Elapsed 2: " + (time3 - time2) + "ms");
+
+ OutputStream output = protocol.getOutputStream();
+ output.write("GET / HTTP/1.1\r\n\r\n".getBytes("UTF-8"));
+ output.flush();
+
+ InputStream input = protocol.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(input));
+
+ String line;
+ while ((line = reader.readLine()) != null)
+ {
+ System.out.println(">>> " + line);
+ }
+
+ protocol.close();
+ }
+
+ static TlsClientProtocol openTlsConnection(InetAddress address, int port, TlsClient client) throws IOException
+ {
+ Socket s = new Socket(address, port);
+ TlsClientProtocol protocol = new TlsClientProtocol(s.getInputStream(), s.getOutputStream(), secureRandom);
+ protocol.connect(client);
+ return protocol;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsPSKProtocolTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsPSKProtocolTest.java
new file mode 100644
index 00000000..f32554a4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsPSKProtocolTest.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public class TlsPSKProtocolTest
+ extends TestCase
+{
+ public void testClientServer() throws Exception
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ PipedInputStream clientRead = new PipedInputStream();
+ PipedInputStream serverRead = new PipedInputStream();
+ PipedOutputStream clientWrite = new PipedOutputStream(serverRead);
+ PipedOutputStream serverWrite = new PipedOutputStream(clientRead);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite, secureRandom);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite, secureRandom);
+
+ ServerThread serverThread = new ServerThread(serverProtocol);
+ serverThread.start();
+
+ MockPSKTlsClient client = new MockPSKTlsClient(null);
+ clientProtocol.connect(client);
+
+ // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+ int length = 1000;
+
+ byte[] data = new byte[length];
+ secureRandom.nextBytes(data);
+
+ OutputStream output = clientProtocol.getOutputStream();
+ output.write(data);
+
+ byte[] echo = new byte[data.length];
+ int count = Streams.readFully(clientProtocol.getInputStream(), echo);
+
+ assertEquals(count, data.length);
+ assertTrue(Arrays.areEqual(data, echo));
+
+ output.close();
+
+ serverThread.join();
+ }
+
+ static class ServerThread
+ extends Thread
+ {
+ private final TlsServerProtocol serverProtocol;
+
+ ServerThread(TlsServerProtocol serverProtocol)
+ {
+ this.serverProtocol = serverProtocol;
+ }
+
+ public void run()
+ {
+ try
+ {
+ MockPSKTlsServer server = new MockPSKTlsServer();
+ serverProtocol.accept(server);
+ Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream());
+ serverProtocol.close();
+ }
+ catch (Exception e)
+ {
+ // throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolTest.java
new file mode 100644
index 00000000..a817c47a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolTest.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public class TlsProtocolTest
+ extends TestCase
+{
+ public void testClientServer()
+ throws Exception
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ PipedInputStream clientRead = new PipedInputStream();
+ PipedInputStream serverRead = new PipedInputStream();
+ PipedOutputStream clientWrite = new PipedOutputStream(serverRead);
+ PipedOutputStream serverWrite = new PipedOutputStream(clientRead);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite, secureRandom);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite, secureRandom);
+
+ ServerThread serverThread = new ServerThread(serverProtocol);
+ serverThread.start();
+
+ MockTlsClient client = new MockTlsClient(null);
+ clientProtocol.connect(client);
+
+ // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+ int length = 1000;
+
+ byte[] data = new byte[length];
+ secureRandom.nextBytes(data);
+
+ OutputStream output = clientProtocol.getOutputStream();
+ output.write(data);
+
+ byte[] echo = new byte[data.length];
+ int count = Streams.readFully(clientProtocol.getInputStream(), echo);
+
+ assertEquals(count, data.length);
+ assertTrue(Arrays.areEqual(data, echo));
+
+ output.close();
+
+ serverThread.join();
+ }
+
+ static class ServerThread
+ extends Thread
+ {
+ private final TlsServerProtocol serverProtocol;
+
+ ServerThread(TlsServerProtocol serverProtocol)
+ {
+ this.serverProtocol = serverProtocol;
+ }
+
+ public void run()
+ {
+ try
+ {
+ MockTlsServer server = new MockTlsServer();
+ serverProtocol.accept(server);
+ Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream());
+ serverProtocol.close();
+ }
+ catch (Exception e)
+ {
+// throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsSRPProtocolTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsSRPProtocolTest.java
new file mode 100644
index 00000000..b0c3c92e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsSRPProtocolTest.java
@@ -0,0 +1,81 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public class TlsSRPProtocolTest
+ extends TestCase
+{
+ public void testClientServer() throws Exception
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ PipedInputStream clientRead = new PipedInputStream();
+ PipedInputStream serverRead = new PipedInputStream();
+ PipedOutputStream clientWrite = new PipedOutputStream(serverRead);
+ PipedOutputStream serverWrite = new PipedOutputStream(clientRead);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite, secureRandom);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite, secureRandom);
+
+ ServerThread serverThread = new ServerThread(serverProtocol);
+ serverThread.start();
+
+ MockSRPTlsClient client = new MockSRPTlsClient(null, MockSRPTlsServer.TEST_IDENTITY, MockSRPTlsServer.TEST_PASSWORD);
+ clientProtocol.connect(client);
+
+ // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+ int length = 1000;
+
+ byte[] data = new byte[length];
+ secureRandom.nextBytes(data);
+
+ OutputStream output = clientProtocol.getOutputStream();
+ output.write(data);
+
+ byte[] echo = new byte[data.length];
+ int count = Streams.readFully(clientProtocol.getInputStream(), echo);
+
+ assertEquals(count, data.length);
+ assertTrue(Arrays.areEqual(data, echo));
+
+ output.close();
+
+ serverThread.join();
+ }
+
+ static class ServerThread
+ extends Thread
+ {
+ private final TlsServerProtocol serverProtocol;
+
+ ServerThread(TlsServerProtocol serverProtocol)
+ {
+ this.serverProtocol = serverProtocol;
+ }
+
+ public void run()
+ {
+ try
+ {
+ MockSRPTlsServer server = new MockSRPTlsServer();
+ serverProtocol.accept(server);
+ Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream());
+ serverProtocol.close();
+ }
+ catch (Exception e)
+ {
+ //throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsServerTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsServerTest.java
new file mode 100644
index 00000000..e2faea71
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsServerTest.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+import org.bouncycastle.util.io.Streams;
+import org.bouncycastle.util.io.TeeOutputStream;
+
+/**
+ * A simple test designed to conduct a TLS handshake with an external TLS client.
+ * <p>
+ * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+ * this package (under 'src/test/resources') for help configuring an external TLS client.
+ * </p>
+ */
+public class TlsServerTest
+{
+ private static final SecureRandom secureRandom = new SecureRandom();
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ InetAddress address = InetAddress.getLocalHost();
+ int port = 5556;
+
+ ServerSocket ss = new ServerSocket(port, 16, address);
+ while (true)
+ {
+ Socket s = ss.accept();
+ System.out.println("--------------------------------------------------------------------------------");
+ System.out.println("Accepted " + s);
+ ServerThread t = new ServerThread(s);
+ t.start();
+ }
+ }
+
+ static class ServerThread
+ extends Thread
+ {
+ private final Socket s;
+
+ ServerThread(Socket s)
+ {
+ this.s = s;
+ }
+
+ public void run()
+ {
+ try
+ {
+ MockTlsServer server = new MockTlsServer();
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(s.getInputStream(), s.getOutputStream(), secureRandom);
+ serverProtocol.accept(server);
+ OutputStream log = new TeeOutputStream(serverProtocol.getOutputStream(), System.out);
+ Streams.pipeAll(serverProtocol.getInputStream(), log);
+ serverProtocol.close();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ finally
+ {
+ try
+ {
+ s.close();
+ }
+ catch (IOException e)
+ {
+ }
+ finally
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestCase.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestCase.java
new file mode 100644
index 00000000..79ad7844
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestCase.java
@@ -0,0 +1,171 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public class TlsTestCase extends TestCase
+{
+ private static void checkTLSVersion(ProtocolVersion version)
+ {
+ if (version != null && !version.isTLS())
+ {
+ throw new IllegalStateException("Non-TLS version");
+ }
+ }
+
+ protected final TlsTestConfig config;
+
+ public TlsTestCase(TlsTestConfig config, String name)
+ {
+ checkTLSVersion(config.clientMinimumVersion);
+ checkTLSVersion(config.clientOfferVersion);
+ checkTLSVersion(config.serverMaximumVersion);
+ checkTLSVersion(config.serverMinimumVersion);
+
+ this.config = config;
+
+ setName(name);
+ }
+
+ protected void runTest() throws Throwable
+ {
+ SecureRandom secureRandom = new SecureRandom();
+
+ PipedInputStream clientRead = new PipedInputStream();
+ PipedInputStream serverRead = new PipedInputStream();
+ PipedOutputStream clientWrite = new PipedOutputStream(serverRead);
+ PipedOutputStream serverWrite = new PipedOutputStream(clientRead);
+
+ NetworkInputStream clientNetIn = new NetworkInputStream(clientRead);
+ NetworkInputStream serverNetIn = new NetworkInputStream(serverRead);
+ NetworkOutputStream clientNetOut = new NetworkOutputStream(clientWrite);
+ NetworkOutputStream serverNetOut = new NetworkOutputStream(serverWrite);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientNetIn, clientNetOut, secureRandom);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverNetIn, serverNetOut, secureRandom);
+
+ TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+ TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+ ServerThread serverThread = new ServerThread(serverProtocol, serverImpl);
+ serverThread.start();
+
+ Exception caught = null;
+ try
+ {
+ clientProtocol.connect(clientImpl);
+
+ // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+ int length = 1000;
+
+ byte[] data = new byte[length];
+ secureRandom.nextBytes(data);
+
+ OutputStream output = clientProtocol.getOutputStream();
+ output.write(data);
+
+ byte[] echo = new byte[data.length];
+ int count = Streams.readFully(clientProtocol.getInputStream(), echo);
+
+ assertEquals(count, data.length);
+ assertTrue(Arrays.areEqual(data, echo));
+
+ output.close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ logException(caught);
+ }
+
+ serverThread.allowExit();
+ serverThread.join();
+
+ assertTrue("Client InputStream not closed", clientNetIn.isClosed());
+ assertTrue("Client OutputStream not closed", clientNetOut.isClosed());
+ assertTrue("Server InputStream not closed", serverNetIn.isClosed());
+ assertTrue("Server OutputStream not closed", serverNetOut.isClosed());
+
+ assertEquals("Client fatal alert connection end", config.expectFatalAlertConnectionEnd, clientImpl.firstFatalAlertConnectionEnd);
+ assertEquals("Server fatal alert connection end", config.expectFatalAlertConnectionEnd, serverImpl.firstFatalAlertConnectionEnd);
+
+ assertEquals("Client fatal alert description", config.expectFatalAlertDescription, clientImpl.firstFatalAlertDescription);
+ assertEquals("Server fatal alert description", config.expectFatalAlertDescription, serverImpl.firstFatalAlertDescription);
+
+ if (config.expectFatalAlertConnectionEnd == -1)
+ {
+ assertNull("Unexpected client exception", caught);
+ assertNull("Unexpected server exception", serverThread.caught);
+ }
+ }
+
+ protected void logException(Exception e)
+ {
+ if (TlsTestConfig.DEBUG)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ class ServerThread extends Thread
+ {
+ protected final TlsServerProtocol serverProtocol;
+ protected final TlsTestServerImpl serverImpl;
+
+ boolean canExit = false;
+ Exception caught = null;
+
+ ServerThread(TlsServerProtocol serverProtocol, TlsTestServerImpl serverImpl)
+ {
+ this.serverProtocol = serverProtocol;
+ this.serverImpl = serverImpl;
+ }
+
+ synchronized void allowExit()
+ {
+ canExit = true;
+ this.notifyAll();
+ }
+
+ public void run()
+ {
+ try
+ {
+ serverProtocol.accept(serverImpl);
+ Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream());
+ serverProtocol.close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ logException(caught);
+ }
+
+ waitExit();
+ }
+
+ protected synchronized void waitExit()
+ {
+ while (!canExit)
+ {
+ try
+ {
+ this.wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientImpl.java
new file mode 100644
index 00000000..43746313
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientImpl.java
@@ -0,0 +1,251 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CertificateRequest;
+import org.bouncycastle.crypto.tls.ClientCertificateType;
+import org.bouncycastle.crypto.tls.ConnectionEnd;
+import org.bouncycastle.crypto.tls.DefaultTlsClient;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
+import org.bouncycastle.crypto.tls.TlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsCredentials;
+import org.bouncycastle.crypto.tls.TlsFatalAlert;
+import org.bouncycastle.crypto.tls.TlsSignerCredentials;
+import org.bouncycastle.util.Arrays;
+
+class TlsTestClientImpl
+ extends DefaultTlsClient
+{
+ protected final TlsTestConfig config;
+
+ protected int firstFatalAlertConnectionEnd = -1;
+ protected short firstFatalAlertDescription = -1;
+
+ TlsTestClientImpl(TlsTestConfig config)
+ {
+ this.config = config;
+ }
+
+ int getFirstFatalAlertConnectionEnd()
+ {
+ return firstFatalAlertConnectionEnd;
+ }
+
+ short getFirstFatalAlertDescription()
+ {
+ return firstFatalAlertDescription;
+ }
+
+ public ProtocolVersion getClientVersion()
+ {
+ if (config.clientOfferVersion != null)
+ {
+ return config.clientOfferVersion;
+ }
+
+ return super.getClientVersion();
+ }
+
+ public ProtocolVersion getMinimumVersion()
+ {
+ if (config.clientMinimumVersion != null)
+ {
+ return config.clientMinimumVersion;
+ }
+
+ return super.getMinimumVersion();
+ }
+
+ public boolean isFallback()
+ {
+ return config.clientFallback;
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+ {
+ firstFatalAlertConnectionEnd = ConnectionEnd.client;
+ firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client raised alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+ {
+ firstFatalAlertConnectionEnd = ConnectionEnd.server;
+ firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client received alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ }
+ }
+
+ public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException
+ {
+ super.notifyServerVersion(serverVersion);
+
+ if (TlsTestConfig.DEBUG)
+ {
+ System.out.println("TLS client negotiated " + serverVersion);
+ }
+ }
+
+ public TlsAuthentication getAuthentication()
+ throws IOException
+ {
+ return new TlsAuthentication()
+ {
+ public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate)
+ throws IOException
+ {
+ boolean isEmpty = serverCertificate == null || serverCertificate.isEmpty();
+
+ Certificate[] chain = serverCertificate.getCertificateList();
+
+ // TODO Cache test resources?
+ if (isEmpty || !(chain[0].equals(TlsTestUtils.loadCertificateResource("x509-server.pem"))
+ || chain[0].equals(TlsTestUtils.loadCertificateResource("x509-server-dsa.pem"))
+ || chain[0].equals(TlsTestUtils.loadCertificateResource("x509-server-ecdsa.pem"))))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ System.out.println("TLS client received server certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+ }
+
+ public TlsCredentials getClientCredentials(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ if (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+ {
+ throw new IllegalStateException();
+ }
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE)
+ {
+ return null;
+ }
+
+ short[] certificateTypes = certificateRequest.getCertificateTypes();
+ if (certificateTypes == null || !Arrays.contains(certificateTypes, ClientCertificateType.rsa_sign))
+ {
+ return null;
+ }
+
+ final TlsSignerCredentials signerCredentials = TlsTestUtils.loadSignerCredentials(context,
+ certificateRequest.getSupportedSignatureAlgorithms(), SignatureAlgorithm.rsa,
+ "x509-client.pem", "x509-client-key.pem");
+
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID)
+ {
+ return signerCredentials;
+ }
+
+ return new TlsSignerCredentials()
+ {
+ public byte[] generateCertificateSignature(byte[] hash) throws IOException
+ {
+ byte[] sig = signerCredentials.generateCertificateSignature(hash);
+
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_VERIFY)
+ {
+ sig = corruptBit(sig);
+ }
+
+ return sig;
+ }
+
+ public org.bouncycastle.crypto.tls.Certificate getCertificate()
+ {
+ org.bouncycastle.crypto.tls.Certificate cert = signerCredentials.getCertificate();
+
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_CERT)
+ {
+ cert = corruptCertificate(cert);
+ }
+
+ return cert;
+ }
+
+ public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm()
+ {
+ return signerCredentials.getSignatureAndHashAlgorithm();
+ }
+ };
+ }
+ };
+ }
+
+ protected org.bouncycastle.crypto.tls.Certificate corruptCertificate(org.bouncycastle.crypto.tls.Certificate cert)
+ {
+ Certificate[] certList = cert.getCertificateList();
+ certList[0] = corruptCertificateSignature(certList[0]);
+ return new org.bouncycastle.crypto.tls.Certificate(certList);
+ }
+
+ protected Certificate corruptCertificateSignature(Certificate cert)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(cert.getTBSCertificate());
+ v.add(cert.getSignatureAlgorithm());
+ v.add(corruptBitString(cert.getSignature()));
+
+ return Certificate.getInstance(new DERSequence(v));
+ }
+
+ protected DERBitString corruptBitString(DERBitString bs)
+ {
+ return new DERBitString(corruptBit(bs.getBytes()));
+ }
+
+ protected byte[] corruptBit(byte[] bs)
+ {
+ bs = Arrays.clone(bs);
+
+ // Flip a random bit
+ int bit = context.getSecureRandom().nextInt(bs.length << 3);
+ bs[bit >>> 3] ^= (1 << (bit & 7));
+
+ return bs;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestConfig.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestConfig.java
new file mode 100644
index 00000000..461ecd5e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestConfig.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.crypto.tls.test;
+
+import org.bouncycastle.crypto.tls.ConnectionEnd;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+
+public class TlsTestConfig
+{
+ public static final boolean DEBUG = false;
+
+ /**
+ * Client does not authenticate, ignores any certificate request
+ */
+ public static final int CLIENT_AUTH_NONE = 0;
+
+ /**
+ * Client will authenticate if it receives a certificate request
+ */
+ public static final int CLIENT_AUTH_VALID = 1;
+
+ /**
+ * Client will authenticate if it receives a certificate request, with an invalid certificate
+ */
+ public static final int CLIENT_AUTH_INVALID_CERT = 2;
+
+ /**
+ * Client will authenticate if it receives a certificate request, with an invalid CertificateVerify signature
+ */
+ public static final int CLIENT_AUTH_INVALID_VERIFY = 3;
+
+ /**
+ * Server will not request a client certificate
+ */
+ public static final int SERVER_CERT_REQ_NONE = 0;
+
+ /**
+ * Server will request a client certificate but receiving one is optional
+ */
+ public static final int SERVER_CERT_REQ_OPTIONAL = 1;
+
+ /**
+ * Server will request a client certificate and receiving one is mandatory
+ */
+ public static final int SERVER_CERT_REQ_MANDATORY = 2;
+
+ /**
+ * Configures the client authentication behaviour of the test client. Use CLIENT_AUTH_* constants.
+ */
+ public int clientAuth = CLIENT_AUTH_VALID;
+
+ /**
+ * Configures the minimum protocol version the client will accept. If null, uses the library's default.
+ */
+ public ProtocolVersion clientMinimumVersion = null;
+
+ /**
+ * Configures the protocol version the client will offer. If null, uses the library's default.
+ */
+ public ProtocolVersion clientOfferVersion = null;
+
+ /**
+ * Configures whether the client will indicate version fallback via TLS_FALLBACK_SCSV.
+ */
+ public boolean clientFallback = false;
+
+ /**
+ * Configures whether the test server will send a certificate request.
+ */
+ public int serverCertReq = SERVER_CERT_REQ_OPTIONAL;
+
+ /**
+ * Configures the maximum protocol version the server will accept. If null, uses the library's default.
+ */
+ public ProtocolVersion serverMaximumVersion = null;
+
+ /**
+ * Configures the minimum protocol version the server will accept. If null, uses the library's default.
+ */
+ public ProtocolVersion serverMinimumVersion = null;
+
+ /**
+ * Configures the connection end that a fatal alert is expected to be raised. Use ConnectionEnd.* constants.
+ */
+ public int expectFatalAlertConnectionEnd = -1;
+
+ /**
+ * Configures the type of fatal alert expected to be raised. Use AlertDescription.* constants.
+ */
+ public short expectFatalAlertDescription = -1;
+
+ public void expectClientFatalAlert(short alertDescription)
+ {
+ this.expectFatalAlertConnectionEnd = ConnectionEnd.client;
+ this.expectFatalAlertDescription = alertDescription;
+ }
+
+ public void expectServerFatalAlert(short alertDescription)
+ {
+ this.expectFatalAlertConnectionEnd = ConnectionEnd.server;
+ this.expectFatalAlertDescription = alertDescription;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerImpl.java
new file mode 100644
index 00000000..b3342934
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerImpl.java
@@ -0,0 +1,198 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CertificateRequest;
+import org.bouncycastle.crypto.tls.ClientCertificateType;
+import org.bouncycastle.crypto.tls.ConnectionEnd;
+import org.bouncycastle.crypto.tls.DefaultTlsServer;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.TlsEncryptionCredentials;
+import org.bouncycastle.crypto.tls.TlsFatalAlert;
+import org.bouncycastle.crypto.tls.TlsSignerCredentials;
+import org.bouncycastle.crypto.tls.TlsUtils;
+
+class TlsTestServerImpl
+ extends DefaultTlsServer
+{
+ protected final TlsTestConfig config;
+
+ protected int firstFatalAlertConnectionEnd = -1;
+ protected short firstFatalAlertDescription = -1;
+
+ TlsTestServerImpl(TlsTestConfig config)
+ {
+ this.config = config;
+ }
+
+ int getFirstFatalAlertConnectionEnd()
+ {
+ return firstFatalAlertConnectionEnd;
+ }
+
+ short getFirstFatalAlertDescription()
+ {
+ return firstFatalAlertDescription;
+ }
+
+ protected ProtocolVersion getMaximumVersion()
+ {
+ if (config.serverMaximumVersion != null)
+ {
+ return config.serverMaximumVersion;
+ }
+
+ return super.getMaximumVersion();
+ }
+
+ protected ProtocolVersion getMinimumVersion()
+ {
+ if (config.serverMinimumVersion != null)
+ {
+ return config.serverMinimumVersion;
+ }
+
+ return super.getMinimumVersion();
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause)
+ {
+ if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+ {
+ firstFatalAlertConnectionEnd = ConnectionEnd.server;
+ firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS server raised alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+ {
+ firstFatalAlertConnectionEnd = ConnectionEnd.client;
+ firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS server received alert: " + AlertLevel.getText(alertLevel)
+ + ", " + AlertDescription.getText(alertDescription));
+ }
+ }
+
+ public ProtocolVersion getServerVersion() throws IOException
+ {
+ ProtocolVersion serverVersion = super.getServerVersion();
+
+ if (TlsTestConfig.DEBUG)
+ {
+ System.out.println("TLS server negotiated " + serverVersion);
+ }
+
+ return serverVersion;
+ }
+
+ public CertificateRequest getCertificateRequest() throws IOException
+ {
+ if (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+ {
+ return null;
+ }
+
+ short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign,
+ ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+ Vector serverSigAlgs = null;
+ if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(serverVersion))
+ {
+ serverSigAlgs = TlsUtils.getDefaultSupportedSignatureAlgorithms();
+ }
+
+ Vector certificateAuthorities = new Vector();
+ certificateAuthorities.add(TlsTestUtils.loadCertificateResource("x509-ca.pem").getSubject());
+
+ return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+ }
+
+ public void notifyClientCertificate(org.bouncycastle.crypto.tls.Certificate clientCertificate)
+ throws IOException
+ {
+ boolean isEmpty = (clientCertificate == null || clientCertificate.isEmpty());
+
+ if (isEmpty != (config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE))
+ {
+ throw new IllegalStateException();
+ }
+ if (isEmpty && (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_MANDATORY))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ Certificate[] chain = clientCertificate.getCertificateList();
+
+ // TODO Cache test resources?
+ if (!isEmpty && !(chain[0].equals(TlsTestUtils.loadCertificateResource("x509-client.pem"))
+ || chain[0].equals(TlsTestUtils.loadCertificateResource("x509-client-dsa.pem"))
+ || chain[0].equals(TlsTestUtils.loadCertificateResource("x509-client-ecdsa.pem"))))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ System.out.println("TLS server received client certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+ }
+
+ protected TlsSignerCredentials getDSASignerCredentials() throws IOException
+ {
+ return TlsTestUtils.loadSignerCredentials(context, supportedSignatureAlgorithms, SignatureAlgorithm.dsa,
+ "x509-server-dsa.pem", "x509-server-key-dsa.pem");
+ }
+
+ protected TlsSignerCredentials getECDSASignerCredentials() throws IOException
+ {
+ return TlsTestUtils.loadSignerCredentials(context, supportedSignatureAlgorithms, SignatureAlgorithm.ecdsa,
+ "x509-server-ecdsa.pem", "x509-server-key-ecdsa.pem");
+ }
+
+ protected TlsEncryptionCredentials getRSAEncryptionCredentials() throws IOException
+ {
+ return TlsTestUtils.loadEncryptionCredentials(context, new String[]{ "x509-server.pem", "x509-ca.pem" },
+ "x509-server-key.pem");
+ }
+
+ protected TlsSignerCredentials getRSASignerCredentials() throws IOException
+ {
+ return TlsTestUtils.loadSignerCredentials(context, supportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+ "x509-server.pem", "x509-server-key.pem");
+ }
+} \ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestSuite.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestSuite.java
new file mode 100644
index 00000000..ae71fafc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestSuite.java
@@ -0,0 +1,111 @@
+package org.bouncycastle.crypto.tls.test;
+
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TlsTestSuite extends TestSuite
+{
+ // Make the access to constants less verbose
+ static abstract class C extends TlsTestConfig {}
+
+ public static Test suite()
+ {
+ TlsTestSuite testSuite = new TlsTestSuite();
+
+ addFallbackTests(testSuite);
+ addVersionTests(testSuite, ProtocolVersion.TLSv10);
+ addVersionTests(testSuite, ProtocolVersion.TLSv11);
+ addVersionTests(testSuite, ProtocolVersion.TLSv12);
+
+ return testSuite;
+ }
+
+ private static void addFallbackTests(TestSuite testSuite)
+ {
+ {
+ TlsTestConfig c = createTlsTestConfig(ProtocolVersion.TLSv12);
+ c.clientFallback = true;
+
+ testSuite.addTest(new TlsTestCase(c, "FallbackGood"));
+ }
+
+ {
+ TlsTestConfig c = createTlsTestConfig(ProtocolVersion.TLSv12);
+ c.clientOfferVersion = ProtocolVersion.TLSv11;
+ c.clientFallback = true;
+ c.expectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+ testSuite.addTest(new TlsTestCase(c, "FallbackBad"));
+ }
+
+ {
+ TlsTestConfig c = createTlsTestConfig(ProtocolVersion.TLSv12);
+ c.clientOfferVersion = ProtocolVersion.TLSv11;
+
+ testSuite.addTest(new TlsTestCase(c, "FallbackNone"));
+ }
+ }
+
+ private static void addVersionTests(TestSuite testSuite, ProtocolVersion version)
+ {
+ String prefix = version.toString().replaceAll("[ \\.]", "") + "_";
+
+ {
+ TlsTestConfig c = createTlsTestConfig(version);
+
+ testSuite.addTest(new TlsTestCase(c, prefix + "GoodDefault"));
+ }
+
+ {
+ TlsTestConfig c = createTlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+ c.expectServerFatalAlert(AlertDescription.decrypt_error);
+
+ testSuite.addTest(new TlsTestCase(c, prefix + "BadCertificateVerify"));
+ }
+
+ {
+ TlsTestConfig c = createTlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+ c.expectServerFatalAlert(AlertDescription.bad_certificate);
+
+ testSuite.addTest(new TlsTestCase(c, prefix + "BadClientCertificate"));
+ }
+
+ {
+ TlsTestConfig c = createTlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+ c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+ c.expectServerFatalAlert(AlertDescription.handshake_failure);
+
+ testSuite.addTest(new TlsTestCase(c, prefix + "BadMandatoryCertReqDeclined"));
+ }
+
+ {
+ TlsTestConfig c = createTlsTestConfig(version);
+ c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+ testSuite.addTest(new TlsTestCase(c, prefix + "GoodNoCertReq"));
+ }
+
+ {
+ TlsTestConfig c = createTlsTestConfig(version);
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+
+ testSuite.addTest(new TlsTestCase(c, prefix + "GoodOptionalCertReqDeclined"));
+ }
+ }
+
+ private static TlsTestConfig createTlsTestConfig(ProtocolVersion version)
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientMinimumVersion = ProtocolVersion.TLSv10;
+ c.clientOfferVersion = ProtocolVersion.TLSv12;
+ c.serverMaximumVersion = version;
+ c.serverMinimumVersion = ProtocolVersion.TLSv10;
+ return c;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestUtils.java
new file mode 100644
index 00000000..a03f8d0b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestUtils.java
@@ -0,0 +1,192 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.crypto.digests.SHA256Digest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
+import org.bouncycastle.crypto.tls.Certificate;
+import org.bouncycastle.crypto.tls.DefaultTlsAgreementCredentials;
+import org.bouncycastle.crypto.tls.DefaultTlsEncryptionCredentials;
+import org.bouncycastle.crypto.tls.DefaultTlsSignerCredentials;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
+import org.bouncycastle.crypto.tls.TlsAgreementCredentials;
+import org.bouncycastle.crypto.tls.TlsContext;
+import org.bouncycastle.crypto.tls.TlsEncryptionCredentials;
+import org.bouncycastle.crypto.tls.TlsSignerCredentials;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemReader;
+
+public class TlsTestUtils
+{
+ static final byte[] rsaCertData = Base64
+ .decode("MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2"
+ + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq"
+ + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA2MDIwNVoXDTEzMDIyNT"
+ + "A2MDM0NVowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw"
+ + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG"
+ + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy"
+ + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCBSAwEgYDVR"
+ + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAHU55Ncz"
+ + "eglREcTg54YLUlGWu2WOYWhit/iM1eeq8Kivro7q98eW52jTuMI3CI5ulqd0hYzshQKQaZ5GDzErMyM=");
+
+ static final byte[] dudRsaCertData = Base64
+ .decode("MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2"
+ + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq"
+ + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA1NDcyOFoXDTEzMDIyNT"
+ + "A1NDkwOFowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw"
+ + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG"
+ + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy"
+ + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCAAEwEgYDVR"
+ + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAJg55PBS"
+ + "weg6obRUKF4FF6fCrWFi6oCYSQ99LWcAeupc5BofW5MstFMhCOaEucuGVqunwT5G7/DweazzCIrSzB0=");
+
+ static String fingerprint(org.bouncycastle.asn1.x509.Certificate c)
+ throws IOException
+ {
+ byte[] der = c.getEncoded();
+ byte[] sha1 = sha256DigestOf(der);
+ byte[] hexBytes = Hex.encode(sha1);
+ String hex = new String(hexBytes, "ASCII").toUpperCase();
+
+ StringBuffer fp = new StringBuffer();
+ int i = 0;
+ fp.append(hex.substring(i, i + 2));
+ while ((i += 2) < hex.length())
+ {
+ fp.append(':');
+ fp.append(hex.substring(i, i + 2));
+ }
+ return fp.toString();
+ }
+
+ static byte[] sha256DigestOf(byte[] input)
+ {
+ SHA256Digest d = new SHA256Digest();
+ d.update(input, 0, input.length);
+ byte[] result = new byte[d.getDigestSize()];
+ d.doFinal(result, 0);
+ return result;
+ }
+
+ static TlsAgreementCredentials loadAgreementCredentials(TlsContext context,
+ String[] certResources, String keyResource)
+ throws IOException
+ {
+ Certificate certificate = loadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = loadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsAgreementCredentials(certificate, privateKey);
+ }
+
+ static TlsEncryptionCredentials loadEncryptionCredentials(TlsContext context,
+ String[] certResources, String keyResource)
+ throws IOException
+ {
+ Certificate certificate = loadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = loadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsEncryptionCredentials(context, certificate, privateKey);
+ }
+
+ static TlsSignerCredentials loadSignerCredentials(TlsContext context, String[] certResources,
+ String keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ throws IOException
+ {
+ Certificate certificate = loadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = loadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsSignerCredentials(context, certificate, privateKey, signatureAndHashAlgorithm);
+ }
+
+ static TlsSignerCredentials loadSignerCredentials(TlsContext context, Vector supportedSignatureAlgorithms,
+ short signatureAlgorithm, String certResource, String keyResource)
+ throws IOException
+ {
+ /*
+ * TODO Note that this code fails to provide default value for the client supported
+ * algorithms if it wasn't sent.
+ */
+
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ if (supportedSignatureAlgorithms != null)
+ {
+ for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
+ {
+ SignatureAndHashAlgorithm alg = (SignatureAndHashAlgorithm)
+ supportedSignatureAlgorithms.elementAt(i);
+ if (alg.getSignature() == signatureAlgorithm)
+ {
+ signatureAndHashAlgorithm = alg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+
+ return TlsTestUtils.loadSignerCredentials(context, new String[]{
+ certResource, "x509-ca.pem" }, keyResource, signatureAndHashAlgorithm);
+ }
+
+ static Certificate loadCertificateChain(String[] resources)
+ throws IOException
+ {
+ org.bouncycastle.asn1.x509.Certificate[] chain = new org.bouncycastle.asn1.x509.Certificate[resources.length];
+ for (int i = 0; i < resources.length; ++i)
+ {
+ chain[i] = loadCertificateResource(resources[i]);
+ }
+ return new Certificate(chain);
+ }
+
+ static org.bouncycastle.asn1.x509.Certificate loadCertificateResource(String resource)
+ throws IOException
+ {
+ PemObject pem = loadPemResource(resource);
+ if (pem.getType().endsWith("CERTIFICATE"))
+ {
+ return org.bouncycastle.asn1.x509.Certificate.getInstance(pem.getContent());
+ }
+ throw new IllegalArgumentException("'resource' doesn't specify a valid certificate");
+ }
+
+ static AsymmetricKeyParameter loadPrivateKeyResource(String resource)
+ throws IOException
+ {
+ PemObject pem = loadPemResource(resource);
+ if (pem.getType().endsWith("RSA PRIVATE KEY"))
+ {
+ RSAPrivateKey rsa = RSAPrivateKey.getInstance(pem.getContent());
+ return new RSAPrivateCrtKeyParameters(rsa.getModulus(), rsa.getPublicExponent(),
+ rsa.getPrivateExponent(), rsa.getPrime1(), rsa.getPrime2(), rsa.getExponent1(),
+ rsa.getExponent2(), rsa.getCoefficient());
+ }
+ if (pem.getType().endsWith("PRIVATE KEY"))
+ {
+ return PrivateKeyFactory.createKey(pem.getContent());
+ }
+ throw new IllegalArgumentException("'resource' doesn't specify a valid private key");
+ }
+
+ static PemObject loadPemResource(String resource)
+ throws IOException
+ {
+ InputStream s = TlsTestUtils.class.getResourceAsStream(resource);
+ PemReader p = new PemReader(new InputStreamReader(s));
+ PemObject o = p.readPemObject();
+ p.close();
+ return o;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/UnreliableDatagramTransport.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/UnreliableDatagramTransport.java
new file mode 100644
index 00000000..bdc205ae
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/UnreliableDatagramTransport.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.util.Random;
+
+import org.bouncycastle.crypto.tls.DatagramTransport;
+
+public class UnreliableDatagramTransport
+ implements DatagramTransport
+{
+
+ private final DatagramTransport transport;
+ private final Random random;
+ private final int percentPacketLossReceiving, percentPacketLossSending;
+
+ public UnreliableDatagramTransport(DatagramTransport transport, Random random,
+ int percentPacketLossReceiving, int percentPacketLossSending)
+ {
+ if (percentPacketLossReceiving < 0 || percentPacketLossReceiving > 100)
+ {
+ throw new IllegalArgumentException("'percentPacketLossReceiving' out of range");
+ }
+ if (percentPacketLossSending < 0 || percentPacketLossSending > 100)
+ {
+ throw new IllegalArgumentException("'percentPacketLossSending' out of range");
+ }
+
+ this.transport = transport;
+ this.random = random;
+ this.percentPacketLossReceiving = percentPacketLossReceiving;
+ this.percentPacketLossSending = percentPacketLossSending;
+ }
+
+ public int getReceiveLimit()
+ throws IOException
+ {
+ return transport.getReceiveLimit();
+ }
+
+ public int getSendLimit()
+ throws IOException
+ {
+ return transport.getSendLimit();
+ }
+
+ public int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+ long endMillis = System.currentTimeMillis() + waitMillis;
+ for (; ; )
+ {
+ int length = transport.receive(buf, off, len, waitMillis);
+ if (length < 0 || !lostPacket(percentPacketLossReceiving))
+ {
+ return length;
+ }
+
+ System.out.println("PACKET LOSS (" + length + " byte packet not received)");
+
+ long now = System.currentTimeMillis();
+ if (now >= endMillis)
+ {
+ return -1;
+ }
+
+ waitMillis = (int)(endMillis - now);
+ }
+ }
+
+ public void send(byte[] buf, int off, int len)
+ throws IOException
+ {
+ if (lostPacket(percentPacketLossSending))
+ {
+ System.out.println("PACKET LOSS (" + len + " byte packet not sent)");
+ }
+ else
+ {
+ transport.send(buf, off, len);
+ }
+ }
+
+ public void close()
+ throws IOException
+ {
+ transport.close();
+ }
+
+ private boolean lostPacket(int percentPacketLoss)
+ {
+ return percentPacketLoss > 0 && random.nextInt(100) < percentPacketLoss;
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/Pack.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/Pack.java
index f0da0bf0..2ded8efc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/Pack.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/Pack.java
@@ -1,5 +1,8 @@
package org.bouncycastle.crypto.util;
+/**
+ * @deprecated use org.bouncycastle.util.pack
+ */
public abstract class Pack
{
public static int bigEndianToInt(byte[] bs, int off)
@@ -114,6 +117,15 @@ public abstract class Pack
}
}
+ public static void littleEndianToInt(byte[] bs, int bOff, int[] ns, int nOff, int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ ns[nOff + i] = littleEndianToInt(bs, bOff);
+ bOff += 4;
+ }
+ }
+
public static byte[] intToLittleEndian(int n)
{
byte[] bs = new byte[4];
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 6bf3399b..92684ecc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -9,7 +9,6 @@ import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.oiw.ElGamalParameter;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.DHParameter;
@@ -23,12 +22,14 @@ import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ElGamalParameters;
import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
@@ -99,7 +100,7 @@ public class PrivateKeyFactory
}
else if (algId.getAlgorithm().equals(OIWObjectIdentifiers.elGamalAlgorithm))
{
- ElGamalParameter params = new ElGamalParameter((ASN1Sequence)algId.getParameters());
+ ElGamalParameter params = ElGamalParameter.getInstance(algId.getParameters());
ASN1Integer derX = (ASN1Integer)keyInfo.parsePrivateKey();
return new ElGamalPrivateKeyParameters(derX.getValue(), new ElGamalParameters(
@@ -124,24 +125,30 @@ public class PrivateKeyFactory
X962Parameters params = new X962Parameters((ASN1Primitive)algId.getParameters());
X9ECParameters x9;
+ ECDomainParameters dParams;
+
if (params.isNamedCurve())
{
- ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
- x9 = ECNamedCurveTable.getByOID(oid);
+ ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
+
+ x9 = CustomNamedCurves.getByOID(oid);
+ if (x9 == null)
+ {
+ x9 = ECNamedCurveTable.getByOID(oid);
+ }
+ dParams = new ECNamedDomainParameters(
+ oid, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
}
else
{
x9 = X9ECParameters.getInstance(params.getParameters());
+ dParams = new ECDomainParameters(
+ x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
}
ECPrivateKey ec = ECPrivateKey.getInstance(keyInfo.parsePrivateKey());
BigInteger d = ec.getKey();
- // TODO We lose any named parameters here
-
- ECDomainParameters dParams = new ECDomainParameters(
- x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
-
return new ECPrivateKeyParameters(d, dParams);
}
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 7b06c3fe..f61815e6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
@@ -18,6 +18,7 @@ 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.ECNamedDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
@@ -54,11 +55,17 @@ public class PrivateKeyInfoFactory
ECPrivateKeyParameters priv = (ECPrivateKeyParameters)privateKey;
ECDomainParameters domainParams = priv.getParameters();
ASN1Encodable params;
+ int orderBitLength;
- // TODO: need to handle named curves
if (domainParams == null)
{
params = new X962Parameters(DERNull.INSTANCE); // Implicitly CA
+ orderBitLength = priv.getD().bitLength(); // TODO: this is as good as currently available, must be a better way...
+ }
+ else if (domainParams instanceof ECNamedDomainParameters)
+ {
+ params = new X962Parameters(((ECNamedDomainParameters)domainParams).getName());
+ orderBitLength = domainParams.getCurve().getOrder().bitLength();
}
else
{
@@ -70,9 +77,10 @@ public class PrivateKeyInfoFactory
domainParams.getSeed());
params = new X962Parameters(ecP);
+ orderBitLength = domainParams.getCurve().getOrder().bitLength();
}
- return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), new ECPrivateKey(priv.getD(), params));
+ return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), new ECPrivateKey(orderBitLength, priv.getD(), params));
}
else
{
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 2d2927b9..cb130954 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -10,7 +10,6 @@ import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.oiw.ElGamalParameter;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -29,6 +28,7 @@ import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.crypto.params.DHPublicKeyParameters;
@@ -36,6 +36,7 @@ import org.bouncycastle.crypto.params.DHValidationParameters;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ElGamalParameters;
import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
@@ -134,7 +135,7 @@ public class PublicKeyFactory
}
else if (algId.getAlgorithm().equals(OIWObjectIdentifiers.elGamalAlgorithm))
{
- ElGamalParameter params = new ElGamalParameter((ASN1Sequence)algId.getParameters());
+ ElGamalParameter params = ElGamalParameter.getInstance(algId.getParameters());
ASN1Integer derY = (ASN1Integer)keyInfo.parsePublicKey();
return new ElGamalPublicKeyParameters(derY.getValue(), new ElGamalParameters(
@@ -160,24 +161,30 @@ public class PublicKeyFactory
X962Parameters params = X962Parameters.getInstance(algId.getParameters());
X9ECParameters x9;
+ ECDomainParameters dParams;
+
if (params.isNamedCurve())
{
ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
- x9 = ECNamedCurveTable.getByOID(oid);
+
+ x9 = CustomNamedCurves.getByOID(oid);
+ if (x9 == null)
+ {
+ x9 = ECNamedCurveTable.getByOID(oid);
+ }
+ dParams = new ECNamedDomainParameters(
+ oid, x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
}
else
{
x9 = X9ECParameters.getInstance(params.getParameters());
+ dParams = new ECDomainParameters(
+ x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
}
ASN1OctetString key = new DEROctetString(keyInfo.getPublicKeyData().getBytes());
X9ECPoint derQ = new X9ECPoint(x9.getCurve(), key);
- // TODO We lose any named parameters here
-
- ECDomainParameters dParams = new ECDomainParameters(
- x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
-
return new ECPublicKeyParameters(derQ.getPoint(), dParams);
}
else
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java
index bdc6cbd1..d2d42037 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java
@@ -17,6 +17,7 @@ import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECNamedDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
@@ -52,11 +53,14 @@ public class SubjectPublicKeyInfoFactory
ECDomainParameters domainParams = pub.getParameters();
ASN1Encodable params;
- // TODO: need to handle named curves
if (domainParams == null)
{
params = new X962Parameters(DERNull.INSTANCE); // Implicitly CA
}
+ else if (domainParams instanceof ECNamedDomainParameters)
+ {
+ params = new X962Parameters(((ECNamedDomainParameters)domainParams).getName());
+ }
else
{
X9ECParameters ecP = new X9ECParameters(
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html
new file mode 100644
index 00000000..787b892e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html
@@ -0,0 +1,5 @@
+<html>
+<body bgcolor="#ffffff">
+Some general utility/conversion classes.
+</body>
+</html>