From c06a37d98d64faa86a21a2b7588e454e31990570 Mon Sep 17 00:00:00 2001 From: psoares33 Date: Sun, 20 Jul 2008 16:50:45 +0000 Subject: [PATCH] Move from CVS. git-svn-id: svn://svn.code.sf.net/p/itextsharp/code/trunk@4 820d3149-562b-4f88-9aa4-a8e61a3485cf --- .../srcbc/crypto/AsymmetricCipherKeyPair.cs | 52 + .../srcbc/crypto/AsymmetricKeyParameter.cs | 47 + .../srcbc/crypto/BufferedAeadBlockCipher.cs | 259 ++ .../crypto/BufferedAsymmetricBlockCipher.cs | 152 + src/core/srcbc/crypto/BufferedBlockCipher.cs | 372 ++ src/core/srcbc/crypto/BufferedCipherBase.cs | 113 + src/core/srcbc/crypto/BufferedIesCipher.cs | 113 + src/core/srcbc/crypto/BufferedStreamCipher.cs | 131 + src/core/srcbc/crypto/CipherKeyGenerator.cs | 83 + src/core/srcbc/crypto/CryptoException.cs | 25 + src/core/srcbc/crypto/DataLengthException.cs | 39 + .../srcbc/crypto/IAsymmetricBlockCipher.cs | 30 + .../IAsymmetricCipherKeyPairGenerator.cs | 24 + src/core/srcbc/crypto/IBasicAgreement.cs | 24 + src/core/srcbc/crypto/IBlockCipher.cs | 36 + src/core/srcbc/crypto/IBufferedCipher.cs | 44 + src/core/srcbc/crypto/ICipherParameters.cs | 11 + src/core/srcbc/crypto/IDSA.cs | 40 + src/core/srcbc/crypto/IDerivationFunction.cs | 24 + .../srcbc/crypto/IDerivationParameters.cs | 11 + src/core/srcbc/crypto/IDigest.cs | 61 + src/core/srcbc/crypto/IMac.cs | 69 + src/core/srcbc/crypto/ISigner.cs | 50 + src/core/srcbc/crypto/ISignerWithRecovery.cs | 28 + src/core/srcbc/crypto/IStreamCipher.cs | 45 + src/core/srcbc/crypto/IWrapper.cs | 18 + .../crypto/InvalidCipherTextException.cs | 37 + .../srcbc/crypto/KeyGenerationParameters.cs | 55 + .../srcbc/crypto/MaxBytesExceededException.cs | 29 + .../srcbc/crypto/PbeParametersGenerator.cs | 169 + src/core/srcbc/crypto/StreamBlockCipher.cs | 109 + .../srcbc/crypto/agreement/DHAgreement.cs | 89 + .../crypto/agreement/DHBasicAgreement.cs | 60 + .../crypto/agreement/ECDHBasicAgreement.cs | 50 + .../crypto/agreement/ECDHCBasicAgreement.cs | 58 + .../agreement/ECDHWithKdfBasicAgreement.cs | 68 + .../crypto/agreement/kdf/DHKdfParameters.cs | 57 + .../crypto/agreement/kdf/DHKekGenerator.cs | 129 + .../crypto/agreement/kdf/ECDHKekGenerator.cs | 70 + .../srcbc/crypto/digests/GOST3411Digest.cs | 338 ++ .../srcbc/crypto/digests/GeneralDigest.cs | 118 + src/core/srcbc/crypto/digests/LongDigest.cs | 380 ++ src/core/srcbc/crypto/digests/MD2Digest.cs | 247 ++ src/core/srcbc/crypto/digests/MD4Digest.cs | 271 ++ src/core/srcbc/crypto/digests/MD5Digest.cs | 301 ++ .../srcbc/crypto/digests/RipeMD128Digest.cs | 462 +++ .../srcbc/crypto/digests/RipeMD160Digest.cs | 423 +++ .../srcbc/crypto/digests/RipeMD256Digest.cs | 409 +++ .../srcbc/crypto/digests/RipeMD320Digest.cs | 438 +++ src/core/srcbc/crypto/digests/Sha1Digest.cs | 285 ++ src/core/srcbc/crypto/digests/Sha224Digest.cs | 287 ++ src/core/srcbc/crypto/digests/Sha256Digest.cs | 310 ++ src/core/srcbc/crypto/digests/Sha384Digest.cs | 85 + src/core/srcbc/crypto/digests/Sha512Digest.cs | 88 + .../srcbc/crypto/digests/ShortenedDigest.cs | 82 + src/core/srcbc/crypto/digests/TigerDigest.cs | 868 +++++ .../srcbc/crypto/digests/WhirlpoolDigest.cs | 397 +++ .../crypto/encodings/ISO9796d1Encoding.cs | 253 ++ .../srcbc/crypto/encodings/OaepEncoding.cs | 334 ++ .../srcbc/crypto/encodings/Pkcs1Encoding.cs | 229 ++ src/core/srcbc/crypto/engines/AesEngine.cs | 550 +++ .../srcbc/crypto/engines/AesFastEngine.cs | 865 +++++ .../srcbc/crypto/engines/AesLightEngine.cs | 438 +++ .../srcbc/crypto/engines/AesWrapEngine.cs | 16 + .../srcbc/crypto/engines/BlowfishEngine.cs | 577 +++ .../srcbc/crypto/engines/CamelliaEngine.cs | 579 +++ .../crypto/engines/CamelliaWrapEngine.cs | 16 + src/core/srcbc/crypto/engines/Cast5Engine.cs | 829 +++++ src/core/srcbc/crypto/engines/Cast6Engine.cs | 277 ++ src/core/srcbc/crypto/engines/DesEdeEngine.cs | 96 + .../srcbc/crypto/engines/DesEdeWrapEngine.cs | 299 ++ src/core/srcbc/crypto/engines/DesEngine.cs | 493 +++ .../srcbc/crypto/engines/ElGamalEngine.cs | 178 + .../srcbc/crypto/engines/GOST28147Engine.cs | 364 ++ src/core/srcbc/crypto/engines/HC128Engine.cs | 241 ++ src/core/srcbc/crypto/engines/HC256Engine.cs | 207 ++ src/core/srcbc/crypto/engines/ISAACEngine.cs | 252 ++ src/core/srcbc/crypto/engines/IdeaEngine.cs | 333 ++ src/core/srcbc/crypto/engines/IesEngine.cs | 236 ++ .../crypto/engines/NaccacheSternEngine.cs | 432 +++ .../srcbc/crypto/engines/NoekeonEngine.cs | 256 ++ src/core/srcbc/crypto/engines/NullEngine.cs | 70 + src/core/srcbc/crypto/engines/RC2Engine.cs | 312 ++ .../srcbc/crypto/engines/RC2WrapEngine.cs | 372 ++ src/core/srcbc/crypto/engines/RC4Engine.cs | 147 + src/core/srcbc/crypto/engines/RC532Engine.cs | 294 ++ src/core/srcbc/crypto/engines/RC564Engine.cs | 295 ++ src/core/srcbc/crypto/engines/RC6Engine.cs | 362 ++ .../srcbc/crypto/engines/RFC3211WrapEngine.cs | 166 + .../srcbc/crypto/engines/RFC3394WrapEngine.cs | 181 + .../srcbc/crypto/engines/RSABlindedEngine.cs | 139 + .../srcbc/crypto/engines/RSABlindingEngine.cs | 139 + .../srcbc/crypto/engines/RSACoreEngine.cs | 156 + .../srcbc/crypto/engines/RijndaelEngine.cs | 740 ++++ src/core/srcbc/crypto/engines/RsaEngine.cs | 78 + src/core/srcbc/crypto/engines/SEEDEngine.cs | 361 ++ .../srcbc/crypto/engines/SEEDWrapEngine.cs | 16 + .../srcbc/crypto/engines/Salsa20Engine.cs | 363 ++ .../srcbc/crypto/engines/SerpentEngine.cs | 779 ++++ .../srcbc/crypto/engines/SkipjackEngine.cs | 255 ++ src/core/srcbc/crypto/engines/TEAEngine.cs | 191 + .../srcbc/crypto/engines/TwofishEngine.cs | 673 ++++ src/core/srcbc/crypto/engines/VMPCEngine.cs | 139 + .../srcbc/crypto/engines/VMPCKSA3Engine.cs | 51 + src/core/srcbc/crypto/engines/XTEAEngine.cs | 185 + .../generators/BaseKdfBytesGenerator.cs | 141 + .../generators/DHBasicKeyPairGenerator.cs | 40 + .../crypto/generators/DHKeyGeneratorHelper.cs | 78 + .../crypto/generators/DHKeyPairGenerator.cs | 40 + .../generators/DHParametersGenerator.cs | 45 + .../crypto/generators/DHParametersHelper.cs | 244 ++ .../crypto/generators/DesEdeKeyGenerator.cs | 66 + .../crypto/generators/DesKeyGenerator.cs | 34 + .../crypto/generators/DsaKeyPairGenerator.cs | 56 + .../generators/DsaParametersGenerator.cs | 184 + .../crypto/generators/ECKeyPairGenerator.cs | 130 + .../generators/ElGamalKeyPairGenerator.cs | 40 + .../generators/ElGamalParametersGenerator.cs | 46 + .../generators/GOST3410KeyPairGenerator.cs | 73 + .../generators/GOST3410ParametersGenerator.cs | 530 +++ .../crypto/generators/Kdf1BytesGenerator.cs | 27 + .../crypto/generators/Kdf2BytesGenerator.cs | 28 + .../crypto/generators/Mgf1BytesGenerator.cs | 117 + .../NaccacheSternKeyPairGenerator.cs | 330 ++ .../OpenSSLPBEParametersGenerator.cs | 167 + .../generators/Pkcs12ParametersGenerator.cs | 245 ++ .../generators/Pkcs5S1ParametersGenerator.cs | 162 + .../generators/Pkcs5S2ParametersGenerator.cs | 175 + .../generators/RSABlindingFactorGenerator.cs | 69 + .../crypto/generators/RsaKeyPairGenerator.cs | 139 + src/core/srcbc/crypto/io/CipherStream.cs | 224 ++ src/core/srcbc/crypto/io/DigestStream.cs | 116 + src/core/srcbc/crypto/io/MacStream.cs | 116 + src/core/srcbc/crypto/macs/CMac.cs | 240 ++ .../srcbc/crypto/macs/CbcBlockCipherMac.cs | 213 ++ .../srcbc/crypto/macs/CfbBlockCipherMac.cs | 368 ++ src/core/srcbc/crypto/macs/GOST28147Mac.cs | 296 ++ src/core/srcbc/crypto/macs/HMac.cs | 141 + src/core/srcbc/crypto/macs/ISO9797Alg3Mac.cs | 259 ++ src/core/srcbc/crypto/macs/VMPCMac.cs | 173 + src/core/srcbc/crypto/modes/CbcBlockCipher.cs | 230 ++ src/core/srcbc/crypto/modes/CcmBlockCipher.cs | 345 ++ src/core/srcbc/crypto/modes/CfbBlockCipher.cs | 218 ++ src/core/srcbc/crypto/modes/CtsBlockCipher.cs | 253 ++ src/core/srcbc/crypto/modes/EAXBlockCipher.cs | 302 ++ src/core/srcbc/crypto/modes/GCMBlockCipher.cs | 447 +++ .../srcbc/crypto/modes/GOFBBlockCipher.cs | 223 ++ .../srcbc/crypto/modes/IAeadBlockCipher.cs | 90 + src/core/srcbc/crypto/modes/OfbBlockCipher.cs | 178 + .../crypto/modes/OpenPgpCfbBlockCipher.cs | 344 ++ src/core/srcbc/crypto/modes/SicBlockCipher.cs | 106 + .../crypto/paddings/BlockCipherPadding.cs | 43 + .../crypto/paddings/ISO10126d2Padding.cs | 76 + .../srcbc/crypto/paddings/ISO7816d4Padding.cs | 79 + .../paddings/PaddedBufferedBlockCipher.cs | 287 ++ .../srcbc/crypto/paddings/Pkcs7Padding.cs | 77 + src/core/srcbc/crypto/paddings/TbcPadding.cs | 79 + src/core/srcbc/crypto/paddings/X923Padding.cs | 82 + .../srcbc/crypto/paddings/ZeroBytePadding.cs | 68 + .../srcbc/crypto/parameters/AEADParameters.cs | 53 + .../srcbc/crypto/parameters/CcmParameters.cs | 25 + .../parameters/DHKeyGenerationParameters.cs | 25 + .../crypto/parameters/DHKeyParameters.cs | 59 + .../srcbc/crypto/parameters/DHParameters.cs | 178 + .../parameters/DHPrivateKeyParameters.cs | 50 + .../parameters/DHPublicKeyParameters.cs | 53 + .../parameters/DHValidationParameters.cs | 59 + .../crypto/parameters/DesEdeParameters.cs | 95 + .../srcbc/crypto/parameters/DesParameters.cs | 130 + .../parameters/DsaKeyGenerationParameters.cs | 26 + .../crypto/parameters/DsaKeyParameters.cs | 59 + .../srcbc/crypto/parameters/DsaParameters.cs | 85 + .../parameters/DsaPrivateKeyParameters.cs | 53 + .../parameters/DsaPublicKeyParameters.cs | 52 + .../parameters/DsaValidationParameters.cs | 59 + .../crypto/parameters/ECDomainParameters.cs | 116 + .../parameters/ECKeyGenerationParameters.cs | 55 + .../crypto/parameters/ECKeyParameters.cs | 121 + .../parameters/ECPrivateKeyParameters.cs | 74 + .../parameters/ECPublicKeyParameters.cs | 73 + .../ElGamalKeyGenerationParameters.cs | 25 + .../crypto/parameters/ElGamalKeyParameters.cs | 59 + .../crypto/parameters/ElGamalParameters.cs | 81 + .../parameters/ElGamalPrivateKeyParameters.cs | 53 + .../parameters/ElGamalPublicKeyParameters.cs | 53 + .../GOST3410KeyGenerationParameters.cs | 55 + .../parameters/GOST3410KeyParameters.cs | 58 + .../crypto/parameters/GOST3410Parameters.cs | 86 + .../GOST3410PrivateKeyParameters.cs | 41 + .../parameters/GOST3410PublicKeyParameters.cs | 40 + .../GOST3410ValidationParameters.cs | 51 + .../parameters/ISO18033KDFParameters.cs | 25 + .../srcbc/crypto/parameters/IesParameters.cs | 49 + .../parameters/IesWithCipherParameters.cs | 33 + .../srcbc/crypto/parameters/KdfParameters.cs | 33 + .../srcbc/crypto/parameters/KeyParameter.cs | 43 + .../srcbc/crypto/parameters/MgfParameters.cs | 31 + .../NaccacheSternKeyGenerationParameters.cs | 101 + .../parameters/NaccacheSternKeyParameters.cs | 44 + .../NaccacheSternPrivateKeyParameters.cs | 56 + .../crypto/parameters/ParametersWithIV.cs | 44 + .../crypto/parameters/ParametersWithRandom.cs | 48 + .../crypto/parameters/ParametersWithSBox.cs | 24 + .../crypto/parameters/ParametersWithSalt.cs | 39 + .../srcbc/crypto/parameters/RC2Parameters.cs | 47 + .../srcbc/crypto/parameters/RC5Parameters.cs | 27 + .../parameters/RSABlindingParameters.cs | 34 + .../parameters/RsaKeyGenerationParameters.cs | 55 + .../crypto/parameters/RsaKeyParameters.cs | 54 + .../parameters/RsaPrivateCrtKeyParameters.cs | 89 + .../crypto/prng/CryptoApiRandomGenerator.cs | 61 + .../crypto/prng/DigestRandomGenerator.cs | 107 + .../srcbc/crypto/prng/IRandomGenerator.cs | 26 + .../crypto/prng/ReversedWindowGenerator.cs | 98 + .../crypto/prng/ThreadedSeedGenerator.cs | 97 + .../srcbc/crypto/prng/VMPCRandomGenerator.cs | 115 + .../srcbc/crypto/signers/DsaDigestSigner.cs | 158 + src/core/srcbc/crypto/signers/DsaSigner.cs | 136 + src/core/srcbc/crypto/signers/ECDsaSigner.cs | 150 + .../srcbc/crypto/signers/ECGOST3410Signer.cs | 154 + src/core/srcbc/crypto/signers/ECNRSigner.cs | 186 + .../crypto/signers/GOST3410DigestSigner.cs | 145 + .../srcbc/crypto/signers/GOST3410Signer.cs | 132 + .../crypto/signers/Iso9796d2PssSigner.cs | 561 +++ .../srcbc/crypto/signers/Iso9796d2Signer.cs | 451 +++ src/core/srcbc/crypto/signers/PssSigner.cs | 299 ++ .../srcbc/crypto/signers/RsaDigestSigner.cs | 213 ++ .../srcbc/crypto/tls/AlwaysValidVerifyer.cs | 23 + src/core/srcbc/crypto/tls/ByteQueue.cs | 125 + src/core/srcbc/crypto/tls/Certificate.cs | 78 + src/core/srcbc/crypto/tls/CombinedHash.cs | 70 + .../srcbc/crypto/tls/ICertificateVerifyer.cs | 17 + src/core/srcbc/crypto/tls/RecordStream.cs | 107 + .../crypto/tls/TlsBlockCipherCipherSuite.cs | 196 + src/core/srcbc/crypto/tls/TlsCipherSuite.cs | 26 + .../srcbc/crypto/tls/TlsCipherSuiteManager.cs | 72 + src/core/srcbc/crypto/tls/TlsException.cs | 11 + src/core/srcbc/crypto/tls/TlsInputStream.cs | 42 + src/core/srcbc/crypto/tls/TlsMac.cs | 86 + .../srcbc/crypto/tls/TlsNullCipherSuite.cs | 45 + src/core/srcbc/crypto/tls/TlsOutputStream.cs | 50 + .../srcbc/crypto/tls/TlsProtocolHandler.cs | 1152 ++++++ src/core/srcbc/crypto/tls/TlsUtilities.cs | 223 ++ src/core/srcbc/math/BigInteger.cs | 3152 +++++++++++++++++ src/core/srcbc/math/ec/ECAlgorithms.cs | 94 + src/core/srcbc/math/ec/ECCurve.cs | 661 ++++ src/core/srcbc/math/ec/ECFieldElement.cs | 1253 +++++++ src/core/srcbc/math/ec/ECPoint.cs | 566 +++ src/core/srcbc/math/ec/IntArray.cs | 486 +++ .../srcbc/math/ec/abc/SimpleBigDecimal.cs | 241 ++ src/core/srcbc/math/ec/abc/Tnaf.cs | 834 +++++ src/core/srcbc/math/ec/abc/ZTauElement.cs | 36 + .../srcbc/math/ec/multiplier/ECMultiplier.cs | 18 + .../math/ec/multiplier/FpNafMultiplier.cs | 39 + .../srcbc/math/ec/multiplier/PreCompInfo.cs | 11 + .../math/ec/multiplier/ReferenceMultiplier.cs | 30 + .../math/ec/multiplier/WNafMultiplier.cs | 241 ++ .../math/ec/multiplier/WNafPreCompInfo.cs | 46 + .../math/ec/multiplier/WTauNafMultiplier.cs | 120 + .../math/ec/multiplier/WTauNafPreCompInfo.cs | 41 + src/core/srcbc/ocsp/BasicOCSPResp.cs | 215 ++ src/core/srcbc/ocsp/BasicOCSPRespGenerator.cs | 318 ++ src/core/srcbc/ocsp/CertificateID.cs | 118 + src/core/srcbc/ocsp/CertificateStatus.cs | 9 + src/core/srcbc/ocsp/OCSPException.cs | 25 + src/core/srcbc/ocsp/OCSPReq.cs | 263 ++ src/core/srcbc/ocsp/OCSPReqGenerator.cs | 242 ++ src/core/srcbc/ocsp/OCSPResp.cs | 100 + src/core/srcbc/ocsp/OCSPRespGenerator.cs | 54 + src/core/srcbc/ocsp/OCSPRespStatus.cs | 22 + src/core/srcbc/ocsp/OCSPUtil.cs | 132 + src/core/srcbc/ocsp/Req.cs | 38 + src/core/srcbc/ocsp/RespData.cs | 60 + src/core/srcbc/ocsp/RespID.cs | 86 + src/core/srcbc/ocsp/RevokedStatus.cs | 58 + src/core/srcbc/ocsp/SingleResp.cs | 81 + src/core/srcbc/ocsp/UnknownStatus.cs | 15 + src/core/srcbc/openpgp/IStreamGenerator.cs | 7 + src/core/srcbc/openpgp/PGPKeyRing.cs | 77 + src/core/srcbc/openpgp/PGPObject.cs | 9 + ...GPUserAttributeSubpacketVectorGenerator.cs | 28 + src/core/srcbc/openpgp/PgpCompressedData.cs | 50 + .../openpgp/PgpCompressedDataGenerator.cs | 177 + .../openpgp/PgpDataValidationException.cs | 15 + src/core/srcbc/openpgp/PgpEncryptedData.cs | 151 + .../openpgp/PgpEncryptedDataGenerator.cs | 495 +++ .../srcbc/openpgp/PgpEncryptedDataList.cs | 71 + src/core/srcbc/openpgp/PgpException.cs | 19 + src/core/srcbc/openpgp/PgpExperimental.cs | 16 + src/core/srcbc/openpgp/PgpKeyFlags.cs | 13 + src/core/srcbc/openpgp/PgpKeyPair.cs | 67 + src/core/srcbc/openpgp/PgpKeyRingGenerator.cs | 166 + .../openpgp/PgpKeyValidationException.cs | 15 + src/core/srcbc/openpgp/PgpLiteralData.cs | 56 + .../srcbc/openpgp/PgpLiteralDataGenerator.cs | 177 + src/core/srcbc/openpgp/PgpMarker.cs | 18 + src/core/srcbc/openpgp/PgpObjectFactory.cs | 130 + src/core/srcbc/openpgp/PgpOnePassSignature.cs | 179 + .../srcbc/openpgp/PgpOnePassSignatureList.cs | 51 + src/core/srcbc/openpgp/PgpPbeEncryptedData.cs | 135 + src/core/srcbc/openpgp/PgpPrivateKey.cs | 42 + src/core/srcbc/openpgp/PgpPublicKey.cs | 835 +++++ .../openpgp/PgpPublicKeyEncryptedData.cs | 233 ++ src/core/srcbc/openpgp/PgpPublicKeyRing.cs | 187 + .../srcbc/openpgp/PgpPublicKeyRingBundle.cs | 269 ++ src/core/srcbc/openpgp/PgpSecretKey.cs | 707 ++++ src/core/srcbc/openpgp/PgpSecretKeyRing.cs | 208 ++ .../srcbc/openpgp/PgpSecretKeyRingBundle.cs | 270 ++ src/core/srcbc/openpgp/PgpSignature.cs | 409 +++ .../srcbc/openpgp/PgpSignatureGenerator.cs | 393 ++ src/core/srcbc/openpgp/PgpSignatureList.cs | 51 + .../openpgp/PgpSignatureSubpacketGenerator.cs | 141 + .../openpgp/PgpSignatureSubpacketVector.cs | 193 + .../PgpUserAttributeSubpacketVector.cs | 81 + src/core/srcbc/openpgp/PgpUtilities.cs | 431 +++ .../srcbc/openpgp/PgpV3SignatureGenerator.cs | 199 ++ .../srcbc/openpgp/WrappedGeneratorStream.cs | 25 + src/core/srcbc/openssl/IPasswordFinder.cs | 9 + src/core/srcbc/openssl/PEMReader.cs | 453 +++ src/core/srcbc/openssl/PEMUtilities.cs | 138 + src/core/srcbc/openssl/PEMWriter.cs | 278 ++ src/core/srcbc/pkcs/AsymmetricKeyEntry.cs | 32 + .../pkcs/EncryptedPrivateKeyInfoFactory.cs | 75 + .../srcbc/pkcs/Pkcs10CertificationRequest.cs | 447 +++ src/core/srcbc/pkcs/Pkcs12Entry.cs | 64 + src/core/srcbc/pkcs/Pkcs12Store.cs | 1129 ++++++ src/core/srcbc/pkcs/PrivateKeyInfoFactory.cs | 211 ++ src/core/srcbc/pkcs/X509CertificateEntry.cs | 32 + src/core/srcbc/security/AgreementUtilities.cs | 99 + src/core/srcbc/security/CipherUtilities.cs | 566 +++ src/core/srcbc/security/DigestUtilities.cs | 150 + src/core/srcbc/security/DotNetUtilities.cs | 163 + .../security/GeneralSecurityException.cs | 26 + src/core/srcbc/security/GeneratorUtilities.cs | 352 ++ .../srcbc/security/InvalidKeyException.cs | 11 + .../security/InvalidParameterException.cs | 11 + src/core/srcbc/security/KeyException.cs | 11 + src/core/srcbc/security/MacUtilities.cs | 220 ++ .../security/NoSuchAlgorithmException.cs | 12 + src/core/srcbc/security/ParameterUtilities.cs | 318 ++ src/core/srcbc/security/PbeUtilities.cs | 554 +++ src/core/srcbc/security/PrivateKeyFactory.cs | 181 + src/core/srcbc/security/PublicKeyFactory.cs | 211 ++ src/core/srcbc/security/SecureRandom.cs | 224 ++ .../security/SecurityUtilityException.cs | 33 + src/core/srcbc/security/SignatureException.cs | 11 + src/core/srcbc/security/SignerUtilities.cs | 517 +++ src/core/srcbc/security/WrapperUtilities.cs | 145 + .../cert/CertificateEncodingException.cs | 11 + .../security/cert/CertificateException.cs | 11 + .../cert/CertificateExpiredException.cs | 11 + .../cert/CertificateNotYetValidException.cs | 11 + .../cert/CertificateParsingException.cs | 11 + src/core/srcbc/security/cert/CrlException.cs | 11 + src/core/srcbc/tsp/GenTimeAccuracy.cs | 33 + src/core/srcbc/tsp/TSPAlgorithms.cs | 47 + src/core/srcbc/tsp/TSPException.cs | 25 + src/core/srcbc/tsp/TSPUtil.cs | 115 + src/core/srcbc/tsp/TSPValidationException.cs | 39 + src/core/srcbc/tsp/TimeStampRequest.cs | 179 + .../srcbc/tsp/TimeStampRequestGenerator.cs | 99 + src/core/srcbc/tsp/TimeStampResponse.cs | 173 + .../srcbc/tsp/TimeStampResponseGenerator.cs | 150 + src/core/srcbc/tsp/TimeStampToken.cs | 304 ++ src/core/srcbc/tsp/TimeStampTokenGenerator.cs | 252 ++ src/core/srcbc/tsp/TimeStampTokenInfo.cs | 102 + src/core/srcbc/util/Arrays.cs | 134 + src/core/srcbc/util/BigIntegers.cs | 28 + src/core/srcbc/util/Platform.cs | 72 + src/core/srcbc/util/Strings.cs | 34 + src/core/srcbc/util/bzip2/BZip2Constants.cs | 139 + .../srcbc/util/bzip2/CBZip2InputStream.cs | 954 +++++ .../srcbc/util/bzip2/CBZip2OutputStream.cs | 1731 +++++++++ src/core/srcbc/util/bzip2/CRC.cs | 170 + .../util/collections/CollectionUtilities.cs | 48 + .../srcbc/util/collections/EmptyEnumerable.cs | 44 + .../srcbc/util/collections/EnumerableProxy.cs | 25 + src/core/srcbc/util/collections/HashSet.cs | 63 + src/core/srcbc/util/collections/ISet.cs | 13 + src/core/srcbc/util/date/DateTimeObject.cs | 25 + src/core/srcbc/util/date/DateTimeUtilities.cs | 47 + src/core/srcbc/util/encoders/Base64.cs | 115 + src/core/srcbc/util/encoders/Base64Encoder.cs | 307 ++ .../srcbc/util/encoders/BufferedDecoder.cs | 117 + .../srcbc/util/encoders/BufferedEncoder.cs | 117 + src/core/srcbc/util/encoders/Hex.cs | 114 + src/core/srcbc/util/encoders/HexEncoder.cs | 164 + src/core/srcbc/util/encoders/HexTranslator.cs | 108 + src/core/srcbc/util/encoders/IEncoder.cs | 18 + src/core/srcbc/util/encoders/Translator.cs | 19 + src/core/srcbc/util/encoders/UrlBase64.cs | 127 + .../srcbc/util/encoders/UrlBase64Encoder.cs | 31 + src/core/srcbc/util/io/BaseInputStream.cs | 47 + src/core/srcbc/util/io/BaseOutputStream.cs | 47 + src/core/srcbc/util/io/PushbackStream.cs | 52 + src/core/srcbc/util/io/Streams.cs | 57 + src/core/srcbc/util/net/IPAddress.cs | 112 + src/core/srcbc/util/zlib/Adler32.cs | 88 + src/core/srcbc/util/zlib/Deflate.cs | 1640 +++++++++ src/core/srcbc/util/zlib/InfBlocks.cs | 618 ++++ src/core/srcbc/util/zlib/InfCodes.cs | 611 ++++ src/core/srcbc/util/zlib/InfTree.cs | 523 +++ src/core/srcbc/util/zlib/Inflate.cs | 387 ++ src/core/srcbc/util/zlib/JZlib.cs | 70 + src/core/srcbc/util/zlib/StaticTree.cs | 152 + src/core/srcbc/util/zlib/Tree.cs | 367 ++ .../srcbc/util/zlib/ZDeflaterOutputStream.cs | 150 + .../srcbc/util/zlib/ZInflaterInputStream.cs | 126 + src/core/srcbc/util/zlib/ZStream.cs | 214 ++ .../srcbc/x509/AttributeCertificateHolder.cs | 420 +++ .../srcbc/x509/AttributeCertificateIssuer.cs | 177 + .../srcbc/x509/IX509AttributeCertificate.cs | 57 + src/core/srcbc/x509/IX509Extension.cs | 27 + src/core/srcbc/x509/PEMParser.cs | 94 + src/core/srcbc/x509/PrincipalUtil.cs | 70 + .../srcbc/x509/SubjectPublicKeyInfoFactory.cs | 178 + src/core/srcbc/x509/X509AttrCertParser.cs | 172 + src/core/srcbc/x509/X509Attribute.cs | 76 + src/core/srcbc/x509/X509CertPairParser.cs | 94 + src/core/srcbc/x509/X509Certificate.cs | 579 +++ src/core/srcbc/x509/X509CertificatePair.cs | 117 + src/core/srcbc/x509/X509CertificateParser.cs | 182 + src/core/srcbc/x509/X509Crl.cs | 409 +++ src/core/srcbc/x509/X509CrlEntry.cs | 201 ++ src/core/srcbc/x509/X509CrlParser.cs | 194 + src/core/srcbc/x509/X509ExtensionBase.cs | 82 + src/core/srcbc/x509/X509KeyUsage.cs | 59 + src/core/srcbc/x509/X509SignatureUtil.cs | 128 + src/core/srcbc/x509/X509Utilities.cs | 183 + .../srcbc/x509/X509V1CertificateGenerator.cs | 205 ++ .../srcbc/x509/X509V2AttributeCertificate.cs | 239 ++ .../X509V2AttributeCertificateGenerator.cs | 180 + src/core/srcbc/x509/X509V2CRLGenerator.cs | 261 ++ .../srcbc/x509/X509V3CertificateGenerator.cs | 303 ++ .../AuthorityKeyIdentifierStructure.cs | 105 + .../SubjectKeyIdentifierStructure.cs | 51 + .../srcbc/x509/extension/X509ExtensionUtil.cs | 88 + src/core/srcbc/x509/store/IX509Selector.cs | 10 + src/core/srcbc/x509/store/IX509Store.cs | 11 + .../srcbc/x509/store/IX509StoreParameters.cs | 8 + .../srcbc/x509/store/NoSuchStoreException.cs | 25 + .../x509/store/X509AttrCertStoreSelector.cs | 376 ++ .../x509/store/X509CertPairStoreSelector.cs | 92 + .../srcbc/x509/store/X509CertStoreSelector.cs | 337 ++ .../srcbc/x509/store/X509CollectionStore.cs | 49 + .../store/X509CollectionStoreParameters.cs | 58 + .../srcbc/x509/store/X509CrlStoreSelector.cs | 283 ++ .../srcbc/x509/store/X509StoreException.cs | 25 + src/core/srcbc/x509/store/X509StoreFactory.cs | 44 + 449 files changed, 81795 insertions(+) create mode 100644 src/core/srcbc/crypto/AsymmetricCipherKeyPair.cs create mode 100644 src/core/srcbc/crypto/AsymmetricKeyParameter.cs create mode 100644 src/core/srcbc/crypto/BufferedAeadBlockCipher.cs create mode 100644 src/core/srcbc/crypto/BufferedAsymmetricBlockCipher.cs create mode 100644 src/core/srcbc/crypto/BufferedBlockCipher.cs create mode 100644 src/core/srcbc/crypto/BufferedCipherBase.cs create mode 100644 src/core/srcbc/crypto/BufferedIesCipher.cs create mode 100644 src/core/srcbc/crypto/BufferedStreamCipher.cs create mode 100644 src/core/srcbc/crypto/CipherKeyGenerator.cs create mode 100644 src/core/srcbc/crypto/CryptoException.cs create mode 100644 src/core/srcbc/crypto/DataLengthException.cs create mode 100644 src/core/srcbc/crypto/IAsymmetricBlockCipher.cs create mode 100644 src/core/srcbc/crypto/IAsymmetricCipherKeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/IBasicAgreement.cs create mode 100644 src/core/srcbc/crypto/IBlockCipher.cs create mode 100644 src/core/srcbc/crypto/IBufferedCipher.cs create mode 100644 src/core/srcbc/crypto/ICipherParameters.cs create mode 100644 src/core/srcbc/crypto/IDSA.cs create mode 100644 src/core/srcbc/crypto/IDerivationFunction.cs create mode 100644 src/core/srcbc/crypto/IDerivationParameters.cs create mode 100644 src/core/srcbc/crypto/IDigest.cs create mode 100644 src/core/srcbc/crypto/IMac.cs create mode 100644 src/core/srcbc/crypto/ISigner.cs create mode 100644 src/core/srcbc/crypto/ISignerWithRecovery.cs create mode 100644 src/core/srcbc/crypto/IStreamCipher.cs create mode 100644 src/core/srcbc/crypto/IWrapper.cs create mode 100644 src/core/srcbc/crypto/InvalidCipherTextException.cs create mode 100644 src/core/srcbc/crypto/KeyGenerationParameters.cs create mode 100644 src/core/srcbc/crypto/MaxBytesExceededException.cs create mode 100644 src/core/srcbc/crypto/PbeParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/StreamBlockCipher.cs create mode 100644 src/core/srcbc/crypto/agreement/DHAgreement.cs create mode 100644 src/core/srcbc/crypto/agreement/DHBasicAgreement.cs create mode 100644 src/core/srcbc/crypto/agreement/ECDHBasicAgreement.cs create mode 100644 src/core/srcbc/crypto/agreement/ECDHCBasicAgreement.cs create mode 100644 src/core/srcbc/crypto/agreement/ECDHWithKdfBasicAgreement.cs create mode 100644 src/core/srcbc/crypto/agreement/kdf/DHKdfParameters.cs create mode 100644 src/core/srcbc/crypto/agreement/kdf/DHKekGenerator.cs create mode 100644 src/core/srcbc/crypto/agreement/kdf/ECDHKekGenerator.cs create mode 100644 src/core/srcbc/crypto/digests/GOST3411Digest.cs create mode 100644 src/core/srcbc/crypto/digests/GeneralDigest.cs create mode 100644 src/core/srcbc/crypto/digests/LongDigest.cs create mode 100644 src/core/srcbc/crypto/digests/MD2Digest.cs create mode 100644 src/core/srcbc/crypto/digests/MD4Digest.cs create mode 100644 src/core/srcbc/crypto/digests/MD5Digest.cs create mode 100644 src/core/srcbc/crypto/digests/RipeMD128Digest.cs create mode 100644 src/core/srcbc/crypto/digests/RipeMD160Digest.cs create mode 100644 src/core/srcbc/crypto/digests/RipeMD256Digest.cs create mode 100644 src/core/srcbc/crypto/digests/RipeMD320Digest.cs create mode 100644 src/core/srcbc/crypto/digests/Sha1Digest.cs create mode 100644 src/core/srcbc/crypto/digests/Sha224Digest.cs create mode 100644 src/core/srcbc/crypto/digests/Sha256Digest.cs create mode 100644 src/core/srcbc/crypto/digests/Sha384Digest.cs create mode 100644 src/core/srcbc/crypto/digests/Sha512Digest.cs create mode 100644 src/core/srcbc/crypto/digests/ShortenedDigest.cs create mode 100644 src/core/srcbc/crypto/digests/TigerDigest.cs create mode 100644 src/core/srcbc/crypto/digests/WhirlpoolDigest.cs create mode 100644 src/core/srcbc/crypto/encodings/ISO9796d1Encoding.cs create mode 100644 src/core/srcbc/crypto/encodings/OaepEncoding.cs create mode 100644 src/core/srcbc/crypto/encodings/Pkcs1Encoding.cs create mode 100644 src/core/srcbc/crypto/engines/AesEngine.cs create mode 100644 src/core/srcbc/crypto/engines/AesFastEngine.cs create mode 100644 src/core/srcbc/crypto/engines/AesLightEngine.cs create mode 100644 src/core/srcbc/crypto/engines/AesWrapEngine.cs create mode 100644 src/core/srcbc/crypto/engines/BlowfishEngine.cs create mode 100644 src/core/srcbc/crypto/engines/CamelliaEngine.cs create mode 100644 src/core/srcbc/crypto/engines/CamelliaWrapEngine.cs create mode 100644 src/core/srcbc/crypto/engines/Cast5Engine.cs create mode 100644 src/core/srcbc/crypto/engines/Cast6Engine.cs create mode 100644 src/core/srcbc/crypto/engines/DesEdeEngine.cs create mode 100644 src/core/srcbc/crypto/engines/DesEdeWrapEngine.cs create mode 100644 src/core/srcbc/crypto/engines/DesEngine.cs create mode 100644 src/core/srcbc/crypto/engines/ElGamalEngine.cs create mode 100644 src/core/srcbc/crypto/engines/GOST28147Engine.cs create mode 100644 src/core/srcbc/crypto/engines/HC128Engine.cs create mode 100644 src/core/srcbc/crypto/engines/HC256Engine.cs create mode 100644 src/core/srcbc/crypto/engines/ISAACEngine.cs create mode 100644 src/core/srcbc/crypto/engines/IdeaEngine.cs create mode 100644 src/core/srcbc/crypto/engines/IesEngine.cs create mode 100644 src/core/srcbc/crypto/engines/NaccacheSternEngine.cs create mode 100644 src/core/srcbc/crypto/engines/NoekeonEngine.cs create mode 100644 src/core/srcbc/crypto/engines/NullEngine.cs create mode 100644 src/core/srcbc/crypto/engines/RC2Engine.cs create mode 100644 src/core/srcbc/crypto/engines/RC2WrapEngine.cs create mode 100644 src/core/srcbc/crypto/engines/RC4Engine.cs create mode 100644 src/core/srcbc/crypto/engines/RC532Engine.cs create mode 100644 src/core/srcbc/crypto/engines/RC564Engine.cs create mode 100644 src/core/srcbc/crypto/engines/RC6Engine.cs create mode 100644 src/core/srcbc/crypto/engines/RFC3211WrapEngine.cs create mode 100644 src/core/srcbc/crypto/engines/RFC3394WrapEngine.cs create mode 100644 src/core/srcbc/crypto/engines/RSABlindedEngine.cs create mode 100644 src/core/srcbc/crypto/engines/RSABlindingEngine.cs create mode 100644 src/core/srcbc/crypto/engines/RSACoreEngine.cs create mode 100644 src/core/srcbc/crypto/engines/RijndaelEngine.cs create mode 100644 src/core/srcbc/crypto/engines/RsaEngine.cs create mode 100644 src/core/srcbc/crypto/engines/SEEDEngine.cs create mode 100644 src/core/srcbc/crypto/engines/SEEDWrapEngine.cs create mode 100644 src/core/srcbc/crypto/engines/Salsa20Engine.cs create mode 100644 src/core/srcbc/crypto/engines/SerpentEngine.cs create mode 100644 src/core/srcbc/crypto/engines/SkipjackEngine.cs create mode 100644 src/core/srcbc/crypto/engines/TEAEngine.cs create mode 100644 src/core/srcbc/crypto/engines/TwofishEngine.cs create mode 100644 src/core/srcbc/crypto/engines/VMPCEngine.cs create mode 100644 src/core/srcbc/crypto/engines/VMPCKSA3Engine.cs create mode 100644 src/core/srcbc/crypto/engines/XTEAEngine.cs create mode 100644 src/core/srcbc/crypto/generators/BaseKdfBytesGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/DHBasicKeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/DHKeyGeneratorHelper.cs create mode 100644 src/core/srcbc/crypto/generators/DHKeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/DHParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/DHParametersHelper.cs create mode 100644 src/core/srcbc/crypto/generators/DesEdeKeyGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/DesKeyGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/DsaKeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/DsaParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/ECKeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/ElGamalKeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/ElGamalParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/GOST3410KeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/GOST3410ParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/Kdf1BytesGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/Kdf2BytesGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/Mgf1BytesGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/NaccacheSternKeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/OpenSSLPBEParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/Pkcs12ParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/Pkcs5S1ParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/Pkcs5S2ParametersGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/RSABlindingFactorGenerator.cs create mode 100644 src/core/srcbc/crypto/generators/RsaKeyPairGenerator.cs create mode 100644 src/core/srcbc/crypto/io/CipherStream.cs create mode 100644 src/core/srcbc/crypto/io/DigestStream.cs create mode 100644 src/core/srcbc/crypto/io/MacStream.cs create mode 100644 src/core/srcbc/crypto/macs/CMac.cs create mode 100644 src/core/srcbc/crypto/macs/CbcBlockCipherMac.cs create mode 100644 src/core/srcbc/crypto/macs/CfbBlockCipherMac.cs create mode 100644 src/core/srcbc/crypto/macs/GOST28147Mac.cs create mode 100644 src/core/srcbc/crypto/macs/HMac.cs create mode 100644 src/core/srcbc/crypto/macs/ISO9797Alg3Mac.cs create mode 100644 src/core/srcbc/crypto/macs/VMPCMac.cs create mode 100644 src/core/srcbc/crypto/modes/CbcBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/CcmBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/CfbBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/CtsBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/EAXBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/GCMBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/GOFBBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/IAeadBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/OfbBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/OpenPgpCfbBlockCipher.cs create mode 100644 src/core/srcbc/crypto/modes/SicBlockCipher.cs create mode 100644 src/core/srcbc/crypto/paddings/BlockCipherPadding.cs create mode 100644 src/core/srcbc/crypto/paddings/ISO10126d2Padding.cs create mode 100644 src/core/srcbc/crypto/paddings/ISO7816d4Padding.cs create mode 100644 src/core/srcbc/crypto/paddings/PaddedBufferedBlockCipher.cs create mode 100644 src/core/srcbc/crypto/paddings/Pkcs7Padding.cs create mode 100644 src/core/srcbc/crypto/paddings/TbcPadding.cs create mode 100644 src/core/srcbc/crypto/paddings/X923Padding.cs create mode 100644 src/core/srcbc/crypto/paddings/ZeroBytePadding.cs create mode 100644 src/core/srcbc/crypto/parameters/AEADParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/CcmParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DHKeyGenerationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DHKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DHParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DHPrivateKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DHPublicKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DHValidationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DesEdeParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DesParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DsaKeyGenerationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DsaKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DsaParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DsaPrivateKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DsaPublicKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/DsaValidationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ECDomainParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ECKeyGenerationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ECKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ECPrivateKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ECPublicKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ElGamalKeyGenerationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ElGamalKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ElGamalParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ElGamalPrivateKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ElGamalPublicKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/GOST3410KeyGenerationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/GOST3410KeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/GOST3410Parameters.cs create mode 100644 src/core/srcbc/crypto/parameters/GOST3410PrivateKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/GOST3410PublicKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/GOST3410ValidationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ISO18033KDFParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/IesParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/IesWithCipherParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/KdfParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/KeyParameter.cs create mode 100644 src/core/srcbc/crypto/parameters/MgfParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/NaccacheSternKeyGenerationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/NaccacheSternKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/NaccacheSternPrivateKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/ParametersWithIV.cs create mode 100644 src/core/srcbc/crypto/parameters/ParametersWithRandom.cs create mode 100644 src/core/srcbc/crypto/parameters/ParametersWithSBox.cs create mode 100644 src/core/srcbc/crypto/parameters/ParametersWithSalt.cs create mode 100644 src/core/srcbc/crypto/parameters/RC2Parameters.cs create mode 100644 src/core/srcbc/crypto/parameters/RC5Parameters.cs create mode 100644 src/core/srcbc/crypto/parameters/RSABlindingParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/RsaKeyGenerationParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/RsaKeyParameters.cs create mode 100644 src/core/srcbc/crypto/parameters/RsaPrivateCrtKeyParameters.cs create mode 100644 src/core/srcbc/crypto/prng/CryptoApiRandomGenerator.cs create mode 100644 src/core/srcbc/crypto/prng/DigestRandomGenerator.cs create mode 100644 src/core/srcbc/crypto/prng/IRandomGenerator.cs create mode 100644 src/core/srcbc/crypto/prng/ReversedWindowGenerator.cs create mode 100644 src/core/srcbc/crypto/prng/ThreadedSeedGenerator.cs create mode 100644 src/core/srcbc/crypto/prng/VMPCRandomGenerator.cs create mode 100644 src/core/srcbc/crypto/signers/DsaDigestSigner.cs create mode 100644 src/core/srcbc/crypto/signers/DsaSigner.cs create mode 100644 src/core/srcbc/crypto/signers/ECDsaSigner.cs create mode 100644 src/core/srcbc/crypto/signers/ECGOST3410Signer.cs create mode 100644 src/core/srcbc/crypto/signers/ECNRSigner.cs create mode 100644 src/core/srcbc/crypto/signers/GOST3410DigestSigner.cs create mode 100644 src/core/srcbc/crypto/signers/GOST3410Signer.cs create mode 100644 src/core/srcbc/crypto/signers/Iso9796d2PssSigner.cs create mode 100644 src/core/srcbc/crypto/signers/Iso9796d2Signer.cs create mode 100644 src/core/srcbc/crypto/signers/PssSigner.cs create mode 100644 src/core/srcbc/crypto/signers/RsaDigestSigner.cs create mode 100644 src/core/srcbc/crypto/tls/AlwaysValidVerifyer.cs create mode 100644 src/core/srcbc/crypto/tls/ByteQueue.cs create mode 100644 src/core/srcbc/crypto/tls/Certificate.cs create mode 100644 src/core/srcbc/crypto/tls/CombinedHash.cs create mode 100644 src/core/srcbc/crypto/tls/ICertificateVerifyer.cs create mode 100644 src/core/srcbc/crypto/tls/RecordStream.cs create mode 100644 src/core/srcbc/crypto/tls/TlsBlockCipherCipherSuite.cs create mode 100644 src/core/srcbc/crypto/tls/TlsCipherSuite.cs create mode 100644 src/core/srcbc/crypto/tls/TlsCipherSuiteManager.cs create mode 100644 src/core/srcbc/crypto/tls/TlsException.cs create mode 100644 src/core/srcbc/crypto/tls/TlsInputStream.cs create mode 100644 src/core/srcbc/crypto/tls/TlsMac.cs create mode 100644 src/core/srcbc/crypto/tls/TlsNullCipherSuite.cs create mode 100644 src/core/srcbc/crypto/tls/TlsOutputStream.cs create mode 100644 src/core/srcbc/crypto/tls/TlsProtocolHandler.cs create mode 100644 src/core/srcbc/crypto/tls/TlsUtilities.cs create mode 100644 src/core/srcbc/math/BigInteger.cs create mode 100644 src/core/srcbc/math/ec/ECAlgorithms.cs create mode 100644 src/core/srcbc/math/ec/ECCurve.cs create mode 100644 src/core/srcbc/math/ec/ECFieldElement.cs create mode 100644 src/core/srcbc/math/ec/ECPoint.cs create mode 100644 src/core/srcbc/math/ec/IntArray.cs create mode 100644 src/core/srcbc/math/ec/abc/SimpleBigDecimal.cs create mode 100644 src/core/srcbc/math/ec/abc/Tnaf.cs create mode 100644 src/core/srcbc/math/ec/abc/ZTauElement.cs create mode 100644 src/core/srcbc/math/ec/multiplier/ECMultiplier.cs create mode 100644 src/core/srcbc/math/ec/multiplier/FpNafMultiplier.cs create mode 100644 src/core/srcbc/math/ec/multiplier/PreCompInfo.cs create mode 100644 src/core/srcbc/math/ec/multiplier/ReferenceMultiplier.cs create mode 100644 src/core/srcbc/math/ec/multiplier/WNafMultiplier.cs create mode 100644 src/core/srcbc/math/ec/multiplier/WNafPreCompInfo.cs create mode 100644 src/core/srcbc/math/ec/multiplier/WTauNafMultiplier.cs create mode 100644 src/core/srcbc/math/ec/multiplier/WTauNafPreCompInfo.cs create mode 100644 src/core/srcbc/ocsp/BasicOCSPResp.cs create mode 100644 src/core/srcbc/ocsp/BasicOCSPRespGenerator.cs create mode 100644 src/core/srcbc/ocsp/CertificateID.cs create mode 100644 src/core/srcbc/ocsp/CertificateStatus.cs create mode 100644 src/core/srcbc/ocsp/OCSPException.cs create mode 100644 src/core/srcbc/ocsp/OCSPReq.cs create mode 100644 src/core/srcbc/ocsp/OCSPReqGenerator.cs create mode 100644 src/core/srcbc/ocsp/OCSPResp.cs create mode 100644 src/core/srcbc/ocsp/OCSPRespGenerator.cs create mode 100644 src/core/srcbc/ocsp/OCSPRespStatus.cs create mode 100644 src/core/srcbc/ocsp/OCSPUtil.cs create mode 100644 src/core/srcbc/ocsp/Req.cs create mode 100644 src/core/srcbc/ocsp/RespData.cs create mode 100644 src/core/srcbc/ocsp/RespID.cs create mode 100644 src/core/srcbc/ocsp/RevokedStatus.cs create mode 100644 src/core/srcbc/ocsp/SingleResp.cs create mode 100644 src/core/srcbc/ocsp/UnknownStatus.cs create mode 100644 src/core/srcbc/openpgp/IStreamGenerator.cs create mode 100644 src/core/srcbc/openpgp/PGPKeyRing.cs create mode 100644 src/core/srcbc/openpgp/PGPObject.cs create mode 100644 src/core/srcbc/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs create mode 100644 src/core/srcbc/openpgp/PgpCompressedData.cs create mode 100644 src/core/srcbc/openpgp/PgpCompressedDataGenerator.cs create mode 100644 src/core/srcbc/openpgp/PgpDataValidationException.cs create mode 100644 src/core/srcbc/openpgp/PgpEncryptedData.cs create mode 100644 src/core/srcbc/openpgp/PgpEncryptedDataGenerator.cs create mode 100644 src/core/srcbc/openpgp/PgpEncryptedDataList.cs create mode 100644 src/core/srcbc/openpgp/PgpException.cs create mode 100644 src/core/srcbc/openpgp/PgpExperimental.cs create mode 100644 src/core/srcbc/openpgp/PgpKeyFlags.cs create mode 100644 src/core/srcbc/openpgp/PgpKeyPair.cs create mode 100644 src/core/srcbc/openpgp/PgpKeyRingGenerator.cs create mode 100644 src/core/srcbc/openpgp/PgpKeyValidationException.cs create mode 100644 src/core/srcbc/openpgp/PgpLiteralData.cs create mode 100644 src/core/srcbc/openpgp/PgpLiteralDataGenerator.cs create mode 100644 src/core/srcbc/openpgp/PgpMarker.cs create mode 100644 src/core/srcbc/openpgp/PgpObjectFactory.cs create mode 100644 src/core/srcbc/openpgp/PgpOnePassSignature.cs create mode 100644 src/core/srcbc/openpgp/PgpOnePassSignatureList.cs create mode 100644 src/core/srcbc/openpgp/PgpPbeEncryptedData.cs create mode 100644 src/core/srcbc/openpgp/PgpPrivateKey.cs create mode 100644 src/core/srcbc/openpgp/PgpPublicKey.cs create mode 100644 src/core/srcbc/openpgp/PgpPublicKeyEncryptedData.cs create mode 100644 src/core/srcbc/openpgp/PgpPublicKeyRing.cs create mode 100644 src/core/srcbc/openpgp/PgpPublicKeyRingBundle.cs create mode 100644 src/core/srcbc/openpgp/PgpSecretKey.cs create mode 100644 src/core/srcbc/openpgp/PgpSecretKeyRing.cs create mode 100644 src/core/srcbc/openpgp/PgpSecretKeyRingBundle.cs create mode 100644 src/core/srcbc/openpgp/PgpSignature.cs create mode 100644 src/core/srcbc/openpgp/PgpSignatureGenerator.cs create mode 100644 src/core/srcbc/openpgp/PgpSignatureList.cs create mode 100644 src/core/srcbc/openpgp/PgpSignatureSubpacketGenerator.cs create mode 100644 src/core/srcbc/openpgp/PgpSignatureSubpacketVector.cs create mode 100644 src/core/srcbc/openpgp/PgpUserAttributeSubpacketVector.cs create mode 100644 src/core/srcbc/openpgp/PgpUtilities.cs create mode 100644 src/core/srcbc/openpgp/PgpV3SignatureGenerator.cs create mode 100644 src/core/srcbc/openpgp/WrappedGeneratorStream.cs create mode 100644 src/core/srcbc/openssl/IPasswordFinder.cs create mode 100644 src/core/srcbc/openssl/PEMReader.cs create mode 100644 src/core/srcbc/openssl/PEMUtilities.cs create mode 100644 src/core/srcbc/openssl/PEMWriter.cs create mode 100644 src/core/srcbc/pkcs/AsymmetricKeyEntry.cs create mode 100644 src/core/srcbc/pkcs/EncryptedPrivateKeyInfoFactory.cs create mode 100644 src/core/srcbc/pkcs/Pkcs10CertificationRequest.cs create mode 100644 src/core/srcbc/pkcs/Pkcs12Entry.cs create mode 100644 src/core/srcbc/pkcs/Pkcs12Store.cs create mode 100644 src/core/srcbc/pkcs/PrivateKeyInfoFactory.cs create mode 100644 src/core/srcbc/pkcs/X509CertificateEntry.cs create mode 100644 src/core/srcbc/security/AgreementUtilities.cs create mode 100644 src/core/srcbc/security/CipherUtilities.cs create mode 100644 src/core/srcbc/security/DigestUtilities.cs create mode 100644 src/core/srcbc/security/DotNetUtilities.cs create mode 100644 src/core/srcbc/security/GeneralSecurityException.cs create mode 100644 src/core/srcbc/security/GeneratorUtilities.cs create mode 100644 src/core/srcbc/security/InvalidKeyException.cs create mode 100644 src/core/srcbc/security/InvalidParameterException.cs create mode 100644 src/core/srcbc/security/KeyException.cs create mode 100644 src/core/srcbc/security/MacUtilities.cs create mode 100644 src/core/srcbc/security/NoSuchAlgorithmException.cs create mode 100644 src/core/srcbc/security/ParameterUtilities.cs create mode 100644 src/core/srcbc/security/PbeUtilities.cs create mode 100644 src/core/srcbc/security/PrivateKeyFactory.cs create mode 100644 src/core/srcbc/security/PublicKeyFactory.cs create mode 100644 src/core/srcbc/security/SecureRandom.cs create mode 100644 src/core/srcbc/security/SecurityUtilityException.cs create mode 100644 src/core/srcbc/security/SignatureException.cs create mode 100644 src/core/srcbc/security/SignerUtilities.cs create mode 100644 src/core/srcbc/security/WrapperUtilities.cs create mode 100644 src/core/srcbc/security/cert/CertificateEncodingException.cs create mode 100644 src/core/srcbc/security/cert/CertificateException.cs create mode 100644 src/core/srcbc/security/cert/CertificateExpiredException.cs create mode 100644 src/core/srcbc/security/cert/CertificateNotYetValidException.cs create mode 100644 src/core/srcbc/security/cert/CertificateParsingException.cs create mode 100644 src/core/srcbc/security/cert/CrlException.cs create mode 100644 src/core/srcbc/tsp/GenTimeAccuracy.cs create mode 100644 src/core/srcbc/tsp/TSPAlgorithms.cs create mode 100644 src/core/srcbc/tsp/TSPException.cs create mode 100644 src/core/srcbc/tsp/TSPUtil.cs create mode 100644 src/core/srcbc/tsp/TSPValidationException.cs create mode 100644 src/core/srcbc/tsp/TimeStampRequest.cs create mode 100644 src/core/srcbc/tsp/TimeStampRequestGenerator.cs create mode 100644 src/core/srcbc/tsp/TimeStampResponse.cs create mode 100644 src/core/srcbc/tsp/TimeStampResponseGenerator.cs create mode 100644 src/core/srcbc/tsp/TimeStampToken.cs create mode 100644 src/core/srcbc/tsp/TimeStampTokenGenerator.cs create mode 100644 src/core/srcbc/tsp/TimeStampTokenInfo.cs create mode 100644 src/core/srcbc/util/Arrays.cs create mode 100644 src/core/srcbc/util/BigIntegers.cs create mode 100644 src/core/srcbc/util/Platform.cs create mode 100644 src/core/srcbc/util/Strings.cs create mode 100644 src/core/srcbc/util/bzip2/BZip2Constants.cs create mode 100644 src/core/srcbc/util/bzip2/CBZip2InputStream.cs create mode 100644 src/core/srcbc/util/bzip2/CBZip2OutputStream.cs create mode 100644 src/core/srcbc/util/bzip2/CRC.cs create mode 100644 src/core/srcbc/util/collections/CollectionUtilities.cs create mode 100644 src/core/srcbc/util/collections/EmptyEnumerable.cs create mode 100644 src/core/srcbc/util/collections/EnumerableProxy.cs create mode 100644 src/core/srcbc/util/collections/HashSet.cs create mode 100644 src/core/srcbc/util/collections/ISet.cs create mode 100644 src/core/srcbc/util/date/DateTimeObject.cs create mode 100644 src/core/srcbc/util/date/DateTimeUtilities.cs create mode 100644 src/core/srcbc/util/encoders/Base64.cs create mode 100644 src/core/srcbc/util/encoders/Base64Encoder.cs create mode 100644 src/core/srcbc/util/encoders/BufferedDecoder.cs create mode 100644 src/core/srcbc/util/encoders/BufferedEncoder.cs create mode 100644 src/core/srcbc/util/encoders/Hex.cs create mode 100644 src/core/srcbc/util/encoders/HexEncoder.cs create mode 100644 src/core/srcbc/util/encoders/HexTranslator.cs create mode 100644 src/core/srcbc/util/encoders/IEncoder.cs create mode 100644 src/core/srcbc/util/encoders/Translator.cs create mode 100644 src/core/srcbc/util/encoders/UrlBase64.cs create mode 100644 src/core/srcbc/util/encoders/UrlBase64Encoder.cs create mode 100644 src/core/srcbc/util/io/BaseInputStream.cs create mode 100644 src/core/srcbc/util/io/BaseOutputStream.cs create mode 100644 src/core/srcbc/util/io/PushbackStream.cs create mode 100644 src/core/srcbc/util/io/Streams.cs create mode 100644 src/core/srcbc/util/net/IPAddress.cs create mode 100644 src/core/srcbc/util/zlib/Adler32.cs create mode 100644 src/core/srcbc/util/zlib/Deflate.cs create mode 100644 src/core/srcbc/util/zlib/InfBlocks.cs create mode 100644 src/core/srcbc/util/zlib/InfCodes.cs create mode 100644 src/core/srcbc/util/zlib/InfTree.cs create mode 100644 src/core/srcbc/util/zlib/Inflate.cs create mode 100644 src/core/srcbc/util/zlib/JZlib.cs create mode 100644 src/core/srcbc/util/zlib/StaticTree.cs create mode 100644 src/core/srcbc/util/zlib/Tree.cs create mode 100644 src/core/srcbc/util/zlib/ZDeflaterOutputStream.cs create mode 100644 src/core/srcbc/util/zlib/ZInflaterInputStream.cs create mode 100644 src/core/srcbc/util/zlib/ZStream.cs create mode 100644 src/core/srcbc/x509/AttributeCertificateHolder.cs create mode 100644 src/core/srcbc/x509/AttributeCertificateIssuer.cs create mode 100644 src/core/srcbc/x509/IX509AttributeCertificate.cs create mode 100644 src/core/srcbc/x509/IX509Extension.cs create mode 100644 src/core/srcbc/x509/PEMParser.cs create mode 100644 src/core/srcbc/x509/PrincipalUtil.cs create mode 100644 src/core/srcbc/x509/SubjectPublicKeyInfoFactory.cs create mode 100644 src/core/srcbc/x509/X509AttrCertParser.cs create mode 100644 src/core/srcbc/x509/X509Attribute.cs create mode 100644 src/core/srcbc/x509/X509CertPairParser.cs create mode 100644 src/core/srcbc/x509/X509Certificate.cs create mode 100644 src/core/srcbc/x509/X509CertificatePair.cs create mode 100644 src/core/srcbc/x509/X509CertificateParser.cs create mode 100644 src/core/srcbc/x509/X509Crl.cs create mode 100644 src/core/srcbc/x509/X509CrlEntry.cs create mode 100644 src/core/srcbc/x509/X509CrlParser.cs create mode 100644 src/core/srcbc/x509/X509ExtensionBase.cs create mode 100644 src/core/srcbc/x509/X509KeyUsage.cs create mode 100644 src/core/srcbc/x509/X509SignatureUtil.cs create mode 100644 src/core/srcbc/x509/X509Utilities.cs create mode 100644 src/core/srcbc/x509/X509V1CertificateGenerator.cs create mode 100644 src/core/srcbc/x509/X509V2AttributeCertificate.cs create mode 100644 src/core/srcbc/x509/X509V2AttributeCertificateGenerator.cs create mode 100644 src/core/srcbc/x509/X509V2CRLGenerator.cs create mode 100644 src/core/srcbc/x509/X509V3CertificateGenerator.cs create mode 100644 src/core/srcbc/x509/extension/AuthorityKeyIdentifierStructure.cs create mode 100644 src/core/srcbc/x509/extension/SubjectKeyIdentifierStructure.cs create mode 100644 src/core/srcbc/x509/extension/X509ExtensionUtil.cs create mode 100644 src/core/srcbc/x509/store/IX509Selector.cs create mode 100644 src/core/srcbc/x509/store/IX509Store.cs create mode 100644 src/core/srcbc/x509/store/IX509StoreParameters.cs create mode 100644 src/core/srcbc/x509/store/NoSuchStoreException.cs create mode 100644 src/core/srcbc/x509/store/X509AttrCertStoreSelector.cs create mode 100644 src/core/srcbc/x509/store/X509CertPairStoreSelector.cs create mode 100644 src/core/srcbc/x509/store/X509CertStoreSelector.cs create mode 100644 src/core/srcbc/x509/store/X509CollectionStore.cs create mode 100644 src/core/srcbc/x509/store/X509CollectionStoreParameters.cs create mode 100644 src/core/srcbc/x509/store/X509CrlStoreSelector.cs create mode 100644 src/core/srcbc/x509/store/X509StoreException.cs create mode 100644 src/core/srcbc/x509/store/X509StoreFactory.cs diff --git a/src/core/srcbc/crypto/AsymmetricCipherKeyPair.cs b/src/core/srcbc/crypto/AsymmetricCipherKeyPair.cs new file mode 100644 index 0000000..dca2522 --- /dev/null +++ b/src/core/srcbc/crypto/AsymmetricCipherKeyPair.cs @@ -0,0 +1,52 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * a holding class for public/private parameter pairs. + */ + public class AsymmetricCipherKeyPair + { + private readonly AsymmetricKeyParameter publicParameter; + private readonly AsymmetricKeyParameter privateParameter; + + /** + * basic constructor. + * + * @param publicParam a public key parameters object. + * @param privateParam the corresponding private key parameters. + */ + public AsymmetricCipherKeyPair( + AsymmetricKeyParameter publicParameter, + AsymmetricKeyParameter privateParameter) + { + if (publicParameter.IsPrivate) + throw new ArgumentException("Expected a public key", "publicParameter"); + if (!privateParameter.IsPrivate) + throw new ArgumentException("Expected a private key", "privateParameter"); + + this.publicParameter = publicParameter; + this.privateParameter = privateParameter; + } + + /** + * return the public key parameters. + * + * @return the public key parameters. + */ + public AsymmetricKeyParameter Public + { + get { return publicParameter; } + } + + /** + * return the private key parameters. + * + * @return the private key parameters. + */ + public AsymmetricKeyParameter Private + { + get { return privateParameter; } + } + } +} diff --git a/src/core/srcbc/crypto/AsymmetricKeyParameter.cs b/src/core/srcbc/crypto/AsymmetricKeyParameter.cs new file mode 100644 index 0000000..335b587 --- /dev/null +++ b/src/core/srcbc/crypto/AsymmetricKeyParameter.cs @@ -0,0 +1,47 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto +{ + public class AsymmetricKeyParameter + : ICipherParameters + { + private readonly bool privateKey; + + public AsymmetricKeyParameter( + bool privateKey) + { + this.privateKey = privateKey; + } + + public bool IsPrivate + { + get { return privateKey; } + } + + public override bool Equals( + object obj) + { + AsymmetricKeyParameter other = obj as AsymmetricKeyParameter; + + if (other == null) + { + return false; + } + + return Equals(other); + } + + protected bool Equals( + AsymmetricKeyParameter other) + { + return privateKey == other.privateKey; + } + + public override int GetHashCode() + { + return privateKey.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/BufferedAeadBlockCipher.cs b/src/core/srcbc/crypto/BufferedAeadBlockCipher.cs new file mode 100644 index 0000000..bd11f15 --- /dev/null +++ b/src/core/srcbc/crypto/BufferedAeadBlockCipher.cs @@ -0,0 +1,259 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The AEAD block ciphers already handle buffering internally, so this class + * just takes care of implementing IBufferedCipher methods. + */ + public class BufferedAeadBlockCipher + : BufferedCipherBase + { + private readonly IAeadBlockCipher cipher; + + public BufferedAeadBlockCipher( + IAeadBlockCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this.cipher = cipher; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + cipher.Init(forEncryption, parameters); + } + + /** + * return the blocksize for the underlying cipher. + * + * @return the blocksize for the underlying cipher. + */ + public override int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + /** + * 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 override int GetUpdateOutputSize( + int length) + { + return cipher.GetUpdateOutputSize(length); + } + + /** + * 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 override int GetOutputSize( + int length) + { + return cipher.GetOutputSize(length); + } + + /** + * process a single byte, producing an output block if neccessary. + * + * @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 DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + return cipher.ProcessByte(input, output, outOff); + } + + public override byte[] ProcessByte( + byte input) + { + int outLength = GetUpdateOutputSize(1); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessByte(input, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (input == null) + throw new ArgumentNullException("input"); + if (length < 1) + return null; + + int outLength = GetUpdateOutputSize(length); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessBytes(input, inOff, length, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + /** + * 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 DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + return cipher.ProcessBytes(input, inOff, length, output, outOff); + } + + public override byte[] DoFinal() + { + byte[] outBytes = EmptyBuffer; + + int length = GetOutputSize(0); + if (length > 0) + { + outBytes = new byte[length]; + + int pos = DoFinal(outBytes, 0); + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + } + + return outBytes; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int inLen) + { + if (input == null) + throw new ArgumentNullException("input"); + + int length = GetOutputSize(inLen); + + byte[] outBytes = EmptyBuffer; + + if (length > 0) + { + outBytes = new byte[length]; + + int pos = (inLen > 0) + ? ProcessBytes(input, inOff, inLen, outBytes, 0) + : 0; + + pos += DoFinal(outBytes, pos); + + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + } + + return outBytes; + } + + /** + * 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 DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @exception InvalidOperationException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if padding is expected and not found. + * @exception DataLengthException if the input is not block size + * aligned. + */ + public override int DoFinal( + byte[] output, + int outOff) + { + return cipher.DoFinal(output, outOff); + } + + /** + * Reset the buffer and cipher. After resetting the object is in the same + * state as it was after the last init (if there was one). + */ + public override void Reset() + { + cipher.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/BufferedAsymmetricBlockCipher.cs b/src/core/srcbc/crypto/BufferedAsymmetricBlockCipher.cs new file mode 100644 index 0000000..0e5bc3e --- /dev/null +++ b/src/core/srcbc/crypto/BufferedAsymmetricBlockCipher.cs @@ -0,0 +1,152 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Engines; + +namespace Org.BouncyCastle.Crypto +{ + /** + * a buffer wrapper for an asymmetric block cipher, allowing input + * to be accumulated in a piecemeal fashion until final processing. + */ + public class BufferedAsymmetricBlockCipher + : BufferedCipherBase + { + private readonly IAsymmetricBlockCipher cipher; + + private byte[] buffer; + private int bufOff; + + /** + * base constructor. + * + * @param cipher the cipher this buffering object wraps. + */ + public BufferedAsymmetricBlockCipher( + IAsymmetricBlockCipher cipher) + { + this.cipher = cipher; + } + + /** + * return the amount of data sitting in the buffer. + * + * @return the amount of data sitting in the buffer. + */ + internal int GetBufferPosition() + { + return bufOff; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public override int GetBlockSize() + { + return cipher.GetInputBlockSize(); + } + + public override int GetOutputSize( + int length) + { + return cipher.GetOutputBlockSize(); + } + + public override int GetUpdateOutputSize( + int length) + { + return 0; + } + + /** + * initialise the buffer and the underlying cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + */ + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + Reset(); + + cipher.Init(forEncryption, parameters); + + // + // we allow for an extra byte where people are using their own padding + // mechanisms on a raw cipher. + // + this.buffer = new byte[cipher.GetInputBlockSize() + (forEncryption ? 1 : 0)]; + this.bufOff = 0; + } + + public override byte[] ProcessByte( + byte input) + { + if (bufOff >= buffer.Length) + throw new DataLengthException("attempt to process message to long for cipher"); + + buffer[bufOff++] = input; + return null; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (length < 1) + return null; + + if (input == null) + throw new ArgumentNullException("input"); + if (bufOff + length > buffer.Length) + throw new DataLengthException("attempt to process message to long for cipher"); + + Array.Copy(input, inOff, buffer, bufOff, length); + bufOff += length; + return null; + } + + /** + * process the contents of the buffer using the underlying + * cipher. + * + * @return the result of the encryption/decryption process on the + * buffer. + * @exception InvalidCipherTextException if we are given a garbage block. + */ + public override byte[] DoFinal() + { + byte[] outBytes = bufOff > 0 + ? cipher.ProcessBlock(buffer, 0, bufOff) + : EmptyBuffer; + + Reset(); + + return outBytes; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int length) + { + ProcessBytes(input, inOff, length); + return DoFinal(); + } + + /// Reset the buffer + public override void Reset() + { + if (buffer != null) + { + Array.Clear(buffer, 0, buffer.Length); + bufOff = 0; + } + } + } +} diff --git a/src/core/srcbc/crypto/BufferedBlockCipher.cs b/src/core/srcbc/crypto/BufferedBlockCipher.cs new file mode 100644 index 0000000..3c6cc49 --- /dev/null +++ b/src/core/srcbc/crypto/BufferedBlockCipher.cs @@ -0,0 +1,372 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto +{ + /** + * A wrapper class that allows block ciphers to be used to process data in + * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the + * buffer is full and more data is being added, or on a doFinal. + *

+ * Note: in the case where the underlying cipher is either a CFB cipher or an + * OFB one the last block may not be a multiple of the block size. + *

+ */ + public class BufferedBlockCipher + : BufferedCipherBase + { + internal byte[] buf; + internal int bufOff; + internal bool forEncryption; + internal IBlockCipher cipher; + + /** + * constructor for subclasses + */ + protected BufferedBlockCipher() + { + } + + /** + * Create a buffered block cipher without padding. + * + * @param cipher the underlying block cipher this buffering object wraps. + * false otherwise. + */ + public BufferedBlockCipher( + IBlockCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this.cipher = cipher; + buf = new byte[cipher.GetBlockSize()]; + bufOff = 0; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + // Note: This doubles as the Init in the event that this cipher is being used as an IWrapper + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + Reset(); + + cipher.Init(forEncryption, parameters); + } + + /** + * return the blocksize for the underlying cipher. + * + * @return the blocksize for the underlying cipher. + */ + public override int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + /** + * 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 override int GetUpdateOutputSize( + int length) + { + int total = length + bufOff; + int leftOver = 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 override int GetOutputSize( + int length) + { + int total = length + bufOff; + int leftOver = total % buf.Length; + if (leftOver == 0) + { + return total; + } + return total - leftOver + buf.Length; + } + + /** + * process a single byte, producing an output block if neccessary. + * + * @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 DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + buf[bufOff++] = input; + + if (bufOff == buf.Length) + { + if ((outOff + buf.Length) > output.Length) + throw new DataLengthException("output buffer too short"); + + bufOff = 0; + return cipher.ProcessBlock(buf, 0, output, outOff); + } + + return 0; + } + + public override byte[] ProcessByte( + byte input) + { + int outLength = GetUpdateOutputSize(1); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessByte(input, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (input == null) + throw new ArgumentNullException("input"); + if (length < 1) + return null; + + int outLength = GetUpdateOutputSize(length); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessBytes(input, inOff, length, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + /** + * 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 DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (length < 1) + { + if (length < 0) + throw new ArgumentException("Can't have a negative input length!"); + + return 0; + } + + int blockSize = GetBlockSize(); + int outLength = GetUpdateOutputSize(length); + + if (outLength > 0) + { + if ((outOff + outLength) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.Length - bufOff; + if (length > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + resultLen += cipher.ProcessBlock(buf, 0, output, outOff); + bufOff = 0; + length -= gapLen; + inOff += gapLen; + while (length > buf.Length) + { + resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen); + length -= blockSize; + inOff += blockSize; + } + } + Array.Copy(input, inOff, buf, bufOff, length); + bufOff += length; + if (bufOff == buf.Length) + { + resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen); + bufOff = 0; + } + return resultLen; + } + + public override byte[] DoFinal() + { + byte[] outBytes = EmptyBuffer; + + int length = GetOutputSize(0); + if (length > 0) + { + outBytes = new byte[length]; + + int pos = DoFinal(outBytes, 0); + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + } + + return outBytes; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int inLen) + { + if (input == null) + throw new ArgumentNullException("input"); + + int length = GetOutputSize(inLen); + + byte[] outBytes = EmptyBuffer; + + if (length > 0) + { + outBytes = new byte[length]; + + int pos = (inLen > 0) + ? ProcessBytes(input, inOff, inLen, outBytes, 0) + : 0; + + pos += DoFinal(outBytes, pos); + + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + } + + return outBytes; + } + + /** + * 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 DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @exception InvalidOperationException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if padding is expected and not found. + * @exception DataLengthException if the input is not block size + * aligned. + */ + public override int DoFinal( + byte[] output, + int outOff) + { + if (bufOff != 0) + { + if (!cipher.IsPartialBlockOkay) + { + throw new DataLengthException("data not block size aligned"); + } + + if (outOff + bufOff > output.Length) + { + throw new DataLengthException("output buffer too short for DoFinal()"); + } + + // NB: Can't copy directly, or we may write too much output + cipher.ProcessBlock(buf, 0, buf, 0); + Array.Copy(buf, 0, output, outOff, bufOff); + } + + int resultLen = bufOff; + + Reset(); + + return resultLen; + } + + /** + * Reset the buffer and cipher. After resetting the object is in the same + * state as it was after the last init (if there was one). + */ + public override void Reset() + { + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + cipher.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/BufferedCipherBase.cs b/src/core/srcbc/crypto/BufferedCipherBase.cs new file mode 100644 index 0000000..f87f38c --- /dev/null +++ b/src/core/srcbc/crypto/BufferedCipherBase.cs @@ -0,0 +1,113 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + public abstract class BufferedCipherBase + : IBufferedCipher + { + protected static readonly byte[] EmptyBuffer = new byte[0]; + + public abstract string AlgorithmName { get; } + + public abstract void Init(bool forEncryption, ICipherParameters parameters); + + public abstract int GetBlockSize(); + + public abstract int GetOutputSize(int inputLen); + public abstract int GetUpdateOutputSize(int inputLen); + + public abstract byte[] ProcessByte(byte input); + + public virtual int ProcessByte( + byte input, + byte[] output, + int outOff) + { + byte[] outBytes = ProcessByte(input); + if (outBytes == null) + return 0; + if (outOff + outBytes.Length > output.Length) + throw new DataLengthException("output buffer too short"); + outBytes.CopyTo(output, outOff); + return outBytes.Length; + } + + public virtual byte[] ProcessBytes( + byte[] input) + { + return ProcessBytes(input, 0, input.Length); + } + + public abstract byte[] ProcessBytes(byte[] input, int inOff, int length); + + public virtual int ProcessBytes( + byte[] input, + byte[] output, + int outOff) + { + return ProcessBytes(input, 0, input.Length, output, outOff); + } + + public virtual int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + byte[] outBytes = ProcessBytes(input, inOff, length); + if (outBytes == null) + return 0; + if (outOff + outBytes.Length > output.Length) + throw new DataLengthException("output buffer too short"); + outBytes.CopyTo(output, outOff); + return outBytes.Length; + } + + public abstract byte[] DoFinal(); + + public virtual byte[] DoFinal( + byte[] input) + { + return DoFinal(input, 0, input.Length); + } + + public abstract byte[] DoFinal( + byte[] input, + int inOff, + int length); + + public virtual int DoFinal( + byte[] output, + int outOff) + { + byte[] outBytes = DoFinal(); + if (outOff + outBytes.Length > output.Length) + throw new DataLengthException("output buffer too short"); + outBytes.CopyTo(output, outOff); + return outBytes.Length; + } + + public virtual int DoFinal( + byte[] input, + byte[] output, + int outOff) + { + return DoFinal(input, 0, input.Length, output, outOff); + } + + public virtual int DoFinal( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + int len = ProcessBytes(input, inOff, length, output, outOff); + len += DoFinal(output, outOff + len); + return len; + } + + public abstract void Reset(); + } +} diff --git a/src/core/srcbc/crypto/BufferedIesCipher.cs b/src/core/srcbc/crypto/BufferedIesCipher.cs new file mode 100644 index 0000000..95555c0 --- /dev/null +++ b/src/core/srcbc/crypto/BufferedIesCipher.cs @@ -0,0 +1,113 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto +{ + public class BufferedIesCipher + : BufferedCipherBase + { + private readonly IesEngine engine; + private bool forEncryption; + private MemoryStream buffer = new MemoryStream(); + + public BufferedIesCipher( + IesEngine engine) + { + if (engine == null) + throw new ArgumentNullException("engine"); + + this.engine = engine; + } + + public override string AlgorithmName + { + // TODO Create IESEngine.AlgorithmName + get { return "IES"; } + } + + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + // TODO + throw Platform.CreateNotImplementedException("IES"); + } + + public override int GetBlockSize() + { + return 0; + } + + public override int GetOutputSize( + int inputLen) + { + if (engine == null) + throw new InvalidOperationException("cipher not initialised"); + + int baseLen = inputLen + (int) buffer.Length; + return forEncryption + ? baseLen + 20 + : baseLen - 20; + } + + public override int GetUpdateOutputSize( + int inputLen) + { + return 0; + } + + public override byte[] ProcessByte( + byte input) + { + buffer.WriteByte(input); + return null; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (input == null) + throw new ArgumentNullException("input"); + if (inOff < 0) + throw new ArgumentException("inOff"); + if (length < 0) + throw new ArgumentException("length"); + if (inOff + length > input.Length) + throw new ArgumentException("invalid offset/length specified for input array"); + + buffer.Write(input, inOff, length); + return null; + } + + public override byte[] DoFinal() + { + byte[] buf = buffer.ToArray(); + + Reset(); + + return engine.ProcessBlock(buf, 0, buf.Length); + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int length) + { + ProcessBytes(input, inOff, length); + return DoFinal(); + } + + public override void Reset() + { + buffer.SetLength(0); + } + } +} diff --git a/src/core/srcbc/crypto/BufferedStreamCipher.cs b/src/core/srcbc/crypto/BufferedStreamCipher.cs new file mode 100644 index 0000000..0c565b7 --- /dev/null +++ b/src/core/srcbc/crypto/BufferedStreamCipher.cs @@ -0,0 +1,131 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto +{ + public class BufferedStreamCipher + : BufferedCipherBase + { + private readonly IStreamCipher cipher; + + public BufferedStreamCipher( + IStreamCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this.cipher = cipher; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + cipher.Init(forEncryption, parameters); + } + + public override int GetBlockSize() + { + return 0; + } + + public override int GetOutputSize( + int inputLen) + { + return inputLen; + } + + public override int GetUpdateOutputSize( + int inputLen) + { + return inputLen; + } + + public override byte[] ProcessByte( + byte input) + { + return new byte[]{ cipher.ReturnByte(input) }; + } + + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + if (outOff >= output.Length) + throw new DataLengthException("output buffer too short"); + + output[outOff] = cipher.ReturnByte(input); + return 1; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (length < 1) + return null; + + byte[] output = new byte[length]; + cipher.ProcessBytes(input, inOff, length, output, 0); + return output; + } + + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (length < 1) + return 0; + + if (length > 0) + { + cipher.ProcessBytes(input, inOff, length, output, outOff); + } + + return length; + } + + public override byte[] DoFinal() + { + Reset(); + + return EmptyBuffer; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int length) + { + if (length < 1) + return EmptyBuffer; + + byte[] output = ProcessBytes(input, inOff, length); + + Reset(); + + return output; + } + + public override void Reset() + { + cipher.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/CipherKeyGenerator.cs b/src/core/srcbc/crypto/CipherKeyGenerator.cs new file mode 100644 index 0000000..afd1982 --- /dev/null +++ b/src/core/srcbc/crypto/CipherKeyGenerator.cs @@ -0,0 +1,83 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The base class for symmetric, or secret, cipher key generators. + */ + public class CipherKeyGenerator + { + protected internal SecureRandom random; + protected internal int strength; + private bool uninitialised = true; + private int defaultStrength; + + public CipherKeyGenerator() + { + } + + internal CipherKeyGenerator( + int defaultStrength) + { + if (defaultStrength < 1) + throw new ArgumentException("strength must be a positive value", "defaultStrength"); + + this.defaultStrength = defaultStrength; + } + + public int DefaultStrength + { + get { return defaultStrength; } + } + + /** + * initialise the key generator. + * + * @param param the parameters to be used for key generation + */ + public void Init( + KeyGenerationParameters parameters) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + this.uninitialised = false; + + engineInit(parameters); + } + + protected virtual void engineInit( + KeyGenerationParameters parameters) + { + this.random = parameters.Random; + this.strength = (parameters.Strength + 7) / 8; + } + + /** + * Generate a secret key. + * + * @return a byte array containing the key value. + */ + public byte[] GenerateKey() + { + if (uninitialised) + { + if (defaultStrength < 1) + throw new InvalidOperationException("Generator has not been initialised"); + + uninitialised = false; + + engineInit(new KeyGenerationParameters(new SecureRandom(), defaultStrength)); + } + + return engineGenerateKey(); + } + + protected virtual byte[] engineGenerateKey() + { + return random.GenerateSeed(strength); + } + } +} diff --git a/src/core/srcbc/crypto/CryptoException.cs b/src/core/srcbc/crypto/CryptoException.cs new file mode 100644 index 0000000..644bbb9 --- /dev/null +++ b/src/core/srcbc/crypto/CryptoException.cs @@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + public abstract class CryptoException + : Exception + { + protected CryptoException() + { + } + + protected CryptoException( + string message) + : base(message) + { + } + + protected CryptoException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/src/core/srcbc/crypto/DataLengthException.cs b/src/core/srcbc/crypto/DataLengthException.cs new file mode 100644 index 0000000..a3476a0 --- /dev/null +++ b/src/core/srcbc/crypto/DataLengthException.cs @@ -0,0 +1,39 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * this exception is thrown if a buffer that is meant to have output + * copied into it turns out to be too short, or if we've been given + * insufficient input. In general this exception will Get thrown rather + * than an ArrayOutOfBounds exception. + */ + public class DataLengthException + : CryptoException + { + /** + * base constructor. + */ + public DataLengthException() + { + } + + /** + * create a DataLengthException with the given message. + * + * @param message the message to be carried with the exception. + */ + public DataLengthException( + string message) + : base(message) + { + } + + public DataLengthException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/src/core/srcbc/crypto/IAsymmetricBlockCipher.cs b/src/core/srcbc/crypto/IAsymmetricBlockCipher.cs new file mode 100644 index 0000000..31c5a85 --- /dev/null +++ b/src/core/srcbc/crypto/IAsymmetricBlockCipher.cs @@ -0,0 +1,30 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// Base interface for a public/private key block cipher. + public interface IAsymmetricBlockCipher + { + /// The name of the algorithm this cipher implements. + string AlgorithmName { get; } + + /// Initialise the cipher. + /// Initialise for encryption if true, for decryption if false. + /// The key or other data required by the cipher. + void Init(bool forEncryption, ICipherParameters parameters); + + /// The maximum size, in bytes, an input block may be. + int GetInputBlockSize(); + + /// The maximum size, in bytes, an output block will be. + int GetOutputBlockSize(); + + /// Process a block. + /// The input buffer. + /// The offset into inBuf that the input block begins. + /// The length of the input block. + /// Input decrypts improperly. + /// Input is too large for the cipher. + byte[] ProcessBlock(byte[] inBuf, int inOff, int inLen); + } +} diff --git a/src/core/srcbc/crypto/IAsymmetricCipherKeyPairGenerator.cs b/src/core/srcbc/crypto/IAsymmetricCipherKeyPairGenerator.cs new file mode 100644 index 0000000..0104315 --- /dev/null +++ b/src/core/srcbc/crypto/IAsymmetricCipherKeyPairGenerator.cs @@ -0,0 +1,24 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * interface that a public/private key pair generator should conform to. + */ + public interface IAsymmetricCipherKeyPairGenerator + { + /** + * intialise the key pair generator. + * + * @param the parameters the key pair is to be initialised with. + */ + void Init(KeyGenerationParameters parameters); + + /** + * return an AsymmetricCipherKeyPair containing the Generated keys. + * + * @return an AsymmetricCipherKeyPair containing the Generated keys. + */ + AsymmetricCipherKeyPair GenerateKeyPair(); + } +} diff --git a/src/core/srcbc/crypto/IBasicAgreement.cs b/src/core/srcbc/crypto/IBasicAgreement.cs new file mode 100644 index 0000000..310c590 --- /dev/null +++ b/src/core/srcbc/crypto/IBasicAgreement.cs @@ -0,0 +1,24 @@ +using System; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The basic interface that basic Diffie-Hellman implementations + * conforms to. + */ + public interface IBasicAgreement + { + /** + * initialise the agreement engine. + */ + void Init(ICipherParameters parameters); + + /** + * given a public key from a given party calculate the next + * message in the agreement sequence. + */ + BigInteger CalculateAgreement(ICipherParameters pubKey); + } + +} diff --git a/src/core/srcbc/crypto/IBlockCipher.cs b/src/core/srcbc/crypto/IBlockCipher.cs new file mode 100644 index 0000000..7abb773 --- /dev/null +++ b/src/core/srcbc/crypto/IBlockCipher.cs @@ -0,0 +1,36 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// Base interface for a symmetric key block cipher. + public interface IBlockCipher + { + /// The name of the algorithm this cipher implements. + string AlgorithmName { get; } + + /// Initialise the cipher. + /// Initialise for encryption if true, for decryption if false. + /// The key or other data required by the cipher. + void Init(bool forEncryption, ICipherParameters parameters); + + /// The block size for this cipher, in bytes. + int GetBlockSize(); + + /// Indicates whether this cipher can handle partial blocks. + bool IsPartialBlockOkay { get; } + + /// Process a block. + /// The input buffer. + /// The offset into inBuf that the input block begins. + /// The output buffer. + /// The offset into outBuf to write the output block. + /// If input block is wrong size, or outBuf too small. + /// The number of bytes processed and produced. + int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff); + + /// + /// Reset the cipher to the same state as it was after the last init (if there was one). + /// + void Reset(); + } +} diff --git a/src/core/srcbc/crypto/IBufferedCipher.cs b/src/core/srcbc/crypto/IBufferedCipher.cs new file mode 100644 index 0000000..207b9f0 --- /dev/null +++ b/src/core/srcbc/crypto/IBufferedCipher.cs @@ -0,0 +1,44 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// Block cipher engines are expected to conform to this interface. + public interface IBufferedCipher + { + /// The name of the algorithm this cipher implements. + string AlgorithmName { get; } + + /// Initialise the cipher. + /// If true the cipher is initialised for encryption, + /// if false for decryption. + /// The key and other data required by the cipher. + void Init(bool forEncryption, ICipherParameters parameters); + + int GetBlockSize(); + + int GetOutputSize(int inputLen); + + int GetUpdateOutputSize(int inputLen); + + byte[] ProcessByte(byte input); + int ProcessByte(byte input, byte[] output, int outOff); + + byte[] ProcessBytes(byte[] input); + byte[] ProcessBytes(byte[] input, int inOff, int length); + int ProcessBytes(byte[] input, byte[] output, int outOff); + int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff); + + byte[] DoFinal(); + byte[] DoFinal(byte[] input); + byte[] DoFinal(byte[] input, int inOff, int length); + int DoFinal(byte[] output, int outOff); + int DoFinal(byte[] input, byte[] output, int outOff); + int DoFinal(byte[] input, int inOff, int length, byte[] output, int outOff); + + /// + /// Reset the cipher. After resetting the cipher is in the same state + /// as it was after the last init (if there was one). + /// + void Reset(); + } +} diff --git a/src/core/srcbc/crypto/ICipherParameters.cs b/src/core/srcbc/crypto/ICipherParameters.cs new file mode 100644 index 0000000..d2805cc --- /dev/null +++ b/src/core/srcbc/crypto/ICipherParameters.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * all parameter classes implement this. + */ + public interface ICipherParameters + { + } +} diff --git a/src/core/srcbc/crypto/IDSA.cs b/src/core/srcbc/crypto/IDSA.cs new file mode 100644 index 0000000..5c879e1 --- /dev/null +++ b/src/core/srcbc/crypto/IDSA.cs @@ -0,0 +1,40 @@ +using System; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto +{ + /** + * interface for classes implementing the Digital Signature Algorithm + */ + public interface IDsa + { + string AlgorithmName { get; } + + /** + * initialise the signer for signature generation or signature + * verification. + * + * @param forSigning true if we are generating a signature, false + * otherwise. + * @param param key parameters for signature generation. + */ + void Init(bool forSigning, ICipherParameters parameters); + + /** + * sign the passed in message (usually the output of a hash function). + * + * @param message the message to be signed. + * @return two big integers representing the r and s values respectively. + */ + BigInteger[] GenerateSignature(byte[] message); + + /** + * verify the message message against the signature values r and s. + * + * @param message the message that was supposed to have been signed. + * @param r the r signature value. + * @param s the s signature value. + */ + bool VerifySignature(byte[] message, BigInteger r, BigInteger s); + } +} diff --git a/src/core/srcbc/crypto/IDerivationFunction.cs b/src/core/srcbc/crypto/IDerivationFunction.cs new file mode 100644 index 0000000..f89c079 --- /dev/null +++ b/src/core/srcbc/crypto/IDerivationFunction.cs @@ -0,0 +1,24 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * base interface for general purpose byte derivation functions. + */ + public interface IDerivationFunction + { + void Init(IDerivationParameters parameters); + + /** + * return the message digest used as the basis for the function + */ + IDigest Digest + { + get; + } + + int GenerateBytes(byte[] output, int outOff, int length); + //throws DataLengthException, ArgumentException; + } + +} diff --git a/src/core/srcbc/crypto/IDerivationParameters.cs b/src/core/srcbc/crypto/IDerivationParameters.cs new file mode 100644 index 0000000..714fc3f --- /dev/null +++ b/src/core/srcbc/crypto/IDerivationParameters.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * Parameters for key/byte stream derivation classes + */ + public interface IDerivationParameters + { + } +} diff --git a/src/core/srcbc/crypto/IDigest.cs b/src/core/srcbc/crypto/IDigest.cs new file mode 100644 index 0000000..b62877a --- /dev/null +++ b/src/core/srcbc/crypto/IDigest.cs @@ -0,0 +1,61 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * interface that a message digest conforms to. + */ + public interface IDigest + { + /** + * return the algorithm name + * + * @return the algorithm name + */ + string AlgorithmName { get; } + + /** + * return the size, in bytes, of the digest produced by this message digest. + * + * @return the size, in bytes, of the digest produced by this message digest. + */ + int GetDigestSize(); + + /** + * return the size, in bytes, of the internal buffer used by this digest. + * + * @return the size, in bytes, of the internal buffer used by this digest. + */ + int GetByteLength(); + + /** + * update the message digest with a single byte. + * + * @param inByte the input byte to be entered. + */ + void Update(byte input); + + /** + * update the message digest with a block of bytes. + * + * @param input the byte array containing the data. + * @param inOff the offset into the byte array where the data starts. + * @param len the length of the data. + */ + void BlockUpdate(byte[] input, int inOff, int length); + + /** + * Close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param output the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + int DoFinal(byte[] output, int outOff); + + /** + * reset the digest back to it's initial state. + */ + void Reset(); + } +} diff --git a/src/core/srcbc/crypto/IMac.cs b/src/core/srcbc/crypto/IMac.cs new file mode 100644 index 0000000..8fea66f --- /dev/null +++ b/src/core/srcbc/crypto/IMac.cs @@ -0,0 +1,69 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The base interface for implementations of message authentication codes (MACs). + */ + public interface IMac + { + /** + * Initialise the MAC. + * + * @param param the key and other data required by the MAC. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + void Init(ICipherParameters parameters); + + /** + * Return the name of the algorithm the MAC implements. + * + * @return the name of the algorithm the MAC implements. + */ + string AlgorithmName { get; } + + /** + * Return the block size for this MAC (in bytes). + * + * @return the block size for this MAC in bytes. + */ + int GetMacSize(); + + /** + * add a single byte to the mac for processing. + * + * @param in the byte to be processed. + * @exception InvalidOperationException if the MAC is not initialised. + */ + void Update(byte input); + + /** + * @param in the array containing the input. + * @param inOff the index in the array the data begins at. + * @param len the length of the input starting at inOff. + * @exception InvalidOperationException if the MAC is not initialised. + * @exception DataLengthException if there isn't enough data in in. + */ + void BlockUpdate(byte[] input, int inOff, int len); + + /** + * Compute the final stage of the MAC writing the output to the out + * parameter. + *

+ * doFinal leaves the MAC in the same state it was after the last init. + *

+ * @param out the array the MAC is to be output to. + * @param outOff the offset into the out buffer the output is to start at. + * @exception DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the MAC is not initialised. + */ + int DoFinal(byte[] output, int outOff); + + /** + * Reset the MAC. At the end of resetting the MAC should be in the + * in the same state it was after the last init (if there was one). + */ + void Reset(); + } +} diff --git a/src/core/srcbc/crypto/ISigner.cs b/src/core/srcbc/crypto/ISigner.cs new file mode 100644 index 0000000..2e13ccb --- /dev/null +++ b/src/core/srcbc/crypto/ISigner.cs @@ -0,0 +1,50 @@ + +using System; +using System.Text; + +namespace Org.BouncyCastle.Crypto +{ + public interface ISigner + { + /** + * Return the name of the algorithm the signer implements. + * + * @return the name of the algorithm the signer implements. + */ + string AlgorithmName { get; } + + /** + * Initialise the signer for signing or verification. + * + * @param forSigning true if for signing, false otherwise + * @param param necessary parameters. + */ + void Init(bool forSigning, ICipherParameters parameters); + + /** + * update the internal digest with the byte b + */ + void Update(byte input); + + /** + * update the internal digest with the byte array in + */ + void BlockUpdate(byte[] input, int inOff, int length); + + /** + * Generate a signature for the message we've been loaded with using + * the key we were initialised with. + */ + byte[] GenerateSignature(); + /** + * return true if the internal state represents the signature described + * in the passed in array. + */ + bool VerifySignature(byte[] signature); + + /** + * reset the internal state + */ + void Reset(); + } +} diff --git a/src/core/srcbc/crypto/ISignerWithRecovery.cs b/src/core/srcbc/crypto/ISignerWithRecovery.cs new file mode 100644 index 0000000..b8bd822 --- /dev/null +++ b/src/core/srcbc/crypto/ISignerWithRecovery.cs @@ -0,0 +1,28 @@ + +using System; +using System.Text; + +namespace Org.BouncyCastle.Crypto +{ + /** + * Signer with message recovery. + */ + public interface ISignerWithRecovery + : ISigner + { + /** + * Returns true if the signer has recovered the full message as + * part of signature verification. + * + * @return true if full message recovered. + */ + bool HasFullMessage(); + + /** + * Returns a reference to what message was recovered (if any). + * + * @return full/partial message, null if nothing. + */ + byte[] GetRecoveredMessage(); + } +} diff --git a/src/core/srcbc/crypto/IStreamCipher.cs b/src/core/srcbc/crypto/IStreamCipher.cs new file mode 100644 index 0000000..2d0e536 --- /dev/null +++ b/src/core/srcbc/crypto/IStreamCipher.cs @@ -0,0 +1,45 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// The interface stream ciphers conform to. + public interface IStreamCipher + { + /// The name of the algorithm this cipher implements. + string AlgorithmName { get; } + + /// Initialise the cipher. + /// If true the cipher is initialised for encryption, + /// if false for decryption. + /// The key and other data required by the cipher. + /// + /// If the parameters argument is inappropriate. + /// + void Init(bool forEncryption, ICipherParameters parameters); + + /// encrypt/decrypt a single byte returning the result. + /// the byte to be processed. + /// the result of processing the input byte. + byte ReturnByte(byte input); + + /// + /// Process a block of bytes from input putting the result into output. + /// + /// The input byte array. + /// + /// The offset into input where the data to be processed starts. + /// + /// The number of bytes to be processed. + /// The output buffer the processed bytes go into. + /// + /// The offset into output the processed data starts at. + /// + /// If the output buffer is too small. + void ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff); + + /// + /// Reset the cipher to the same state as it was after the last init (if there was one). + /// + void Reset(); + } +} diff --git a/src/core/srcbc/crypto/IWrapper.cs b/src/core/srcbc/crypto/IWrapper.cs new file mode 100644 index 0000000..8e963cb --- /dev/null +++ b/src/core/srcbc/crypto/IWrapper.cs @@ -0,0 +1,18 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto +{ + public interface IWrapper + { + /// The name of the algorithm this cipher implements. + string AlgorithmName { get; } + + void Init(bool forWrapping, ICipherParameters parameters); + + byte[] Wrap(byte[] input, int inOff, int length); + + byte[] Unwrap(byte[] input, int inOff, int length); + } +} diff --git a/src/core/srcbc/crypto/InvalidCipherTextException.cs b/src/core/srcbc/crypto/InvalidCipherTextException.cs new file mode 100644 index 0000000..e862c73 --- /dev/null +++ b/src/core/srcbc/crypto/InvalidCipherTextException.cs @@ -0,0 +1,37 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * this exception is thrown whenever we find something we don't expect in a + * message. + */ + public class InvalidCipherTextException + : CryptoException + { + /** + * base constructor. + */ + public InvalidCipherTextException() + { + } + + /** + * create a InvalidCipherTextException with the given message. + * + * @param message the message to be carried with the exception. + */ + public InvalidCipherTextException( + string message) + : base(message) + { + } + + public InvalidCipherTextException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/src/core/srcbc/crypto/KeyGenerationParameters.cs b/src/core/srcbc/crypto/KeyGenerationParameters.cs new file mode 100644 index 0000000..7ae0d83 --- /dev/null +++ b/src/core/srcbc/crypto/KeyGenerationParameters.cs @@ -0,0 +1,55 @@ +using System; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The base class for parameters to key generators. + */ + public class KeyGenerationParameters + { + private SecureRandom random; + private int strength; + + /** + * initialise the generator with a source of randomness + * and a strength (in bits). + * + * @param random the random byte source. + * @param strength the size, in bits, of the keys we want to produce. + */ + public KeyGenerationParameters( + SecureRandom random, + int strength) + { + if (random == null) + throw new ArgumentNullException("random"); + if (strength < 1) + throw new ArgumentException("strength must be a positive value", "strength"); + + this.random = random; + this.strength = strength; + } + + /** + * return the random source associated with this + * generator. + * + * @return the generators random source. + */ + public SecureRandom Random + { + get { return random; } + } + + /** + * return the bit strength for keys produced by this generator, + * + * @return the strength of the keys this generator produces (in bits). + */ + public int Strength + { + get { return strength; } + } + } +} diff --git a/src/core/srcbc/crypto/MaxBytesExceededException.cs b/src/core/srcbc/crypto/MaxBytesExceededException.cs new file mode 100644 index 0000000..ba8c40a --- /dev/null +++ b/src/core/srcbc/crypto/MaxBytesExceededException.cs @@ -0,0 +1,29 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// + /// This exception is thrown whenever a cipher requires a change of key, iv + /// or similar after x amount of bytes enciphered + /// + public class MaxBytesExceededException + : CryptoException + { + public MaxBytesExceededException() + { + } + + public MaxBytesExceededException( + string message) + : base(message) + { + } + + public MaxBytesExceededException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/src/core/srcbc/crypto/PbeParametersGenerator.cs b/src/core/srcbc/crypto/PbeParametersGenerator.cs new file mode 100644 index 0000000..cefc897 --- /dev/null +++ b/src/core/srcbc/crypto/PbeParametersGenerator.cs @@ -0,0 +1,169 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto +{ + /** + * super class for all Password Based Encyrption (Pbe) parameter generator classes. + */ + public abstract class PbeParametersGenerator + { + protected byte[] mPassword; + protected byte[] mSalt; + protected int mIterationCount; + + /** + * base constructor. + */ + protected PbeParametersGenerator() + { + } + + /** + * initialise the Pbe generator. + * + * @param password the password converted into bytes (see below). + * @param salt the salt to be mixed with the password. + * @param iterationCount the number of iterations the "mixing" function + * is to be applied for. + */ + public virtual void Init( + byte[] password, + byte[] salt, + int iterationCount) + { + if (password == null) + throw new ArgumentNullException("password"); + if (salt == null) + throw new ArgumentNullException("salt"); + + this.mPassword = Arrays.Clone(password); + this.mSalt = Arrays.Clone(salt); + this.mIterationCount = iterationCount; + } + + public virtual byte[] Password + { + get { return Arrays.Clone(mPassword); } + } + + /** + * return the password byte array. + * + * @return the password byte array. + */ + [Obsolete("Use 'Password' property")] + public byte[] GetPassword() + { + return Password; + } + + public virtual byte[] Salt + { + get { return Arrays.Clone(mSalt); } + } + + /** + * return the salt byte array. + * + * @return the salt byte array. + */ + [Obsolete("Use 'Salt' property")] + public byte[] GetSalt() + { + return Salt; + } + + /** + * return the iteration count. + * + * @return the iteration count. + */ + public virtual int IterationCount + { + get { return mIterationCount; } + } + + /** + * Generate derived parameters for a key of length keySize. + * + * @param keySize the length, in bits, of the key required. + * @return a parameters object representing a key. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public abstract ICipherParameters GenerateDerivedParameters(int keySize); + public abstract ICipherParameters GenerateDerivedParameters(string algorithm, int keySize); + + /** + * Generate derived parameters for a key of length keySize, and + * an initialisation vector (IV) of length ivSize. + * + * @param keySize the length, in bits, of the key required. + * @param ivSize the length, in bits, of the iv required. + * @return a parameters object representing a key and an IV. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public abstract ICipherParameters GenerateDerivedParameters(int keySize, int ivSize); + public abstract ICipherParameters GenerateDerivedParameters(string algorithm, int keySize, int ivSize); + + /** + * Generate derived parameters for a key of length keySize, specifically + * for use with a MAC. + * + * @param keySize the length, in bits, of the key required. + * @return a parameters object representing a key. + */ + public abstract ICipherParameters GenerateDerivedMacParameters(int keySize); + + /** + * converts a password to a byte array according to the scheme in + * Pkcs5 (ascii, no padding) + * + * @param password a character array reqpresenting the password. + * @return a byte array representing the password. + */ + public static byte[] Pkcs5PasswordToBytes( + char[] password) + { + return Encoding.ASCII.GetBytes(password); + } + + public static byte[] Pkcs5PasswordToBytes( + string password) + { + return Encoding.ASCII.GetBytes(password); + } + + /** + * converts a password to a byte array according to the scheme in + * Pkcs12 (unicode, big endian, 2 zero pad bytes at the end). + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] Pkcs12PasswordToBytes( + char[] password) + { + return Pkcs12PasswordToBytes(password, false); + } + + public static byte[] Pkcs12PasswordToBytes( + char[] password, + bool wrongPkcs12Zero) + { + if (password.Length < 1) + { + return new byte[wrongPkcs12Zero ? 2 : 0]; + } + + // +1 for extra 2 pad bytes. + byte[] bytes = new byte[(password.Length + 1) * 2]; + + Encoding.BigEndianUnicode.GetBytes(password, 0, password.Length, bytes, 0); + + return bytes; + } + } +} diff --git a/src/core/srcbc/crypto/StreamBlockCipher.cs b/src/core/srcbc/crypto/StreamBlockCipher.cs new file mode 100644 index 0000000..14af8bd --- /dev/null +++ b/src/core/srcbc/crypto/StreamBlockCipher.cs @@ -0,0 +1,109 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto +{ + /** + * a wrapper for block ciphers with a single byte block size, so that they + * can be treated like stream ciphers. + */ + public class StreamBlockCipher + : IStreamCipher + { + private readonly IBlockCipher cipher; + private readonly byte[] oneByte = new byte[1]; + + /** + * basic constructor. + * + * @param cipher the block cipher to be wrapped. + * @exception ArgumentException if the cipher has a block size other than + * one. + */ + public StreamBlockCipher( + IBlockCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + if (cipher.GetBlockSize() != 1) + throw new ArgumentException("block cipher block size != 1.", "cipher"); + + this.cipher = cipher; + } + + /** + * initialise the underlying cipher. + * + * @param forEncryption true if we are setting up for encryption, false otherwise. + * @param param the necessary parameters for the underlying cipher to be initialised. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + cipher.Init(forEncryption, parameters); + } + + /** + * return the name of the algorithm we are wrapping. + * + * @return the name of the algorithm we are wrapping. + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + /** + * 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 input) + { + oneByte[0] = input; + + 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[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (outOff + length > output.Length) + throw new DataLengthException("output buffer too small in ProcessBytes()"); + + for (int i = 0; i != length; i++) + { + cipher.ProcessBlock(input, inOff + i, output, outOff + i); + } + } + + /** + * 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(); + } + } +} diff --git a/src/core/srcbc/crypto/agreement/DHAgreement.cs b/src/core/srcbc/crypto/agreement/DHAgreement.cs new file mode 100644 index 0000000..0812c6c --- /dev/null +++ b/src/core/srcbc/crypto/agreement/DHAgreement.cs @@ -0,0 +1,89 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /** + * a Diffie-Hellman key exchange engine. + *

+ * note: This uses MTI/A0 key agreement in order to make the key agreement + * secure against passive attacks. If you're doing Diffie-Hellman and both + * parties have long term public keys you should look at using this. For + * further information have a look at RFC 2631.

+ *

+ * It's possible to extend this to more than two parties as well, for the moment + * that is left as an exercise for the reader.

+ */ + public class DHAgreement + { + private DHPrivateKeyParameters key; + private DHParameters dhParams; + private BigInteger privateValue; + private SecureRandom random; + + public void Init( + ICipherParameters parameters) + { + AsymmetricKeyParameter kParam; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + kParam = (AsymmetricKeyParameter)rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + kParam = (AsymmetricKeyParameter)parameters; + } + + if (!(kParam is DHPrivateKeyParameters)) + { + throw new ArgumentException("DHEngine expects DHPrivateKeyParameters"); + } + + this.key = (DHPrivateKeyParameters)kParam; + this.dhParams = key.Parameters; + } + + /** + * calculate our initial message. + */ + public BigInteger CalculateMessage() + { + int bits = dhParams.P.BitLength - 1; + + // TODO Should the generated numbers always have length 'P.BitLength - 1'? + this.privateValue = new BigInteger(bits, random).SetBit(bits - 1); + + return dhParams.G.ModPow(privateValue, dhParams.P); + } + + /** + * given a message from a given party and the corresponding public key + * calculate the next message in the agreement sequence. In this case + * this will represent the shared secret. + */ + public BigInteger CalculateAgreement( + DHPublicKeyParameters pub, + BigInteger message) + { + if (pub == null) + throw new ArgumentNullException("pub"); + if (message == null) + throw new ArgumentNullException("message"); + + if (!pub.Parameters.Equals(dhParams)) + { + throw new ArgumentException("Diffie-Hellman public key has wrong parameters."); + } + + return message.ModPow(key.X, dhParams.P).Multiply(pub.Y.ModPow(privateValue, dhParams.P)).Mod(dhParams.P); + } + } +} diff --git a/src/core/srcbc/crypto/agreement/DHBasicAgreement.cs b/src/core/srcbc/crypto/agreement/DHBasicAgreement.cs new file mode 100644 index 0000000..3a2f3e2 --- /dev/null +++ b/src/core/srcbc/crypto/agreement/DHBasicAgreement.cs @@ -0,0 +1,60 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /** + * a Diffie-Hellman key agreement class. + *

+ * note: This is only the basic algorithm, it doesn't take advantage of + * long term public keys if they are available. See the DHAgreement class + * for a "better" implementation.

+ */ + public class DHBasicAgreement + : IBasicAgreement + { + private DHPrivateKeyParameters key; + private DHParameters dhParams; + + public void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + if (!(parameters is DHPrivateKeyParameters)) + { + throw new ArgumentException("DHEngine expects DHPrivateKeyParameters"); + } + + this.key = (DHPrivateKeyParameters) parameters; + this.dhParams = key.Parameters; + } + + /** + * given a short term public key from a given party calculate the next + * message in the agreement sequence. + */ + public BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + if (this.key == null) + throw new InvalidOperationException("Agreement algorithm not initialised"); + + DHPublicKeyParameters pub = (DHPublicKeyParameters)pubKey; + + if (!pub.Parameters.Equals(dhParams)) + { + throw new ArgumentException("Diffie-Hellman public key has wrong parameters."); + } + + return pub.Y.ModPow(key.X, dhParams.P); + } + } + +} diff --git a/src/core/srcbc/crypto/agreement/ECDHBasicAgreement.cs b/src/core/srcbc/crypto/agreement/ECDHBasicAgreement.cs new file mode 100644 index 0000000..162bfa8 --- /dev/null +++ b/src/core/srcbc/crypto/agreement/ECDHBasicAgreement.cs @@ -0,0 +1,50 @@ +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /** + * P1363 7.2.1 ECSVDP-DH + * + * ECSVDP-DH is Elliptic Curve Secret Value Derivation Primitive, + * Diffie-Hellman version. It is based on the work of [DH76], [Mil86], + * and [Kob87]. This primitive derives a shared secret value from one + * party's private key and another party's public key, where both have + * the same set of EC domain parameters. If two parties correctly + * execute this primitive, they will produce the same output. This + * primitive can be invoked by a scheme to derive a shared secret key; + * specifically, it may be used with the schemes ECKAS-DH1 and + * DL/ECKAS-DH2. It assumes that the input keys are valid (see also + * Section 7.2.2). + */ + public class ECDHBasicAgreement + : IBasicAgreement + { + private ECPrivateKeyParameters key; + + public void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + this.key = (ECPrivateKeyParameters) parameters; + } + + public virtual BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey; + ECPoint P = pub.Q.Multiply(key.D); + + // if ( p.IsInfinity ) throw new Exception("d*Q == infinity"); + + return P.X.ToBigInteger(); + } + } + +} diff --git a/src/core/srcbc/crypto/agreement/ECDHCBasicAgreement.cs b/src/core/srcbc/crypto/agreement/ECDHCBasicAgreement.cs new file mode 100644 index 0000000..3bfa3e3 --- /dev/null +++ b/src/core/srcbc/crypto/agreement/ECDHCBasicAgreement.cs @@ -0,0 +1,58 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /** + * P1363 7.2.2 ECSVDP-DHC + * + * ECSVDP-DHC is Elliptic Curve Secret Value Derivation Primitive, + * Diffie-Hellman version with cofactor multiplication. It is based on + * the work of [DH76], [Mil86], [Kob87], [LMQ98] and [Kal98a]. This + * primitive derives a shared secret value from one party's private key + * and another party's public key, where both have the same set of EC + * domain parameters. If two parties correctly execute this primitive, + * they will produce the same output. This primitive can be invoked by a + * scheme to derive a shared secret key; specifically, it may be used + * with the schemes ECKAS-DH1 and DL/ECKAS-DH2. It does not assume the + * validity of the input public key (see also Section 7.2.1). + *

+ * Note: As stated P1363 compatibility mode with ECDH can be preset, and + * in this case the implementation doesn't have a ECDH compatibility mode + * (if you want that just use ECDHBasicAgreement and note they both implement + * BasicAgreement!).

+ */ + public class ECDHCBasicAgreement + : IBasicAgreement + { + private ECPrivateKeyParameters key; + + public void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + this.key = (ECPrivateKeyParameters)parameters; + } + + public BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey; + ECDomainParameters parameters = pub.Parameters; + ECPoint P = pub.Q.Multiply(parameters.H.Multiply(key.D)); + + // if ( p.IsInfinity ) throw new Exception("Invalid public key"); + + return P.X.ToBigInteger(); + } + } + +} diff --git a/src/core/srcbc/crypto/agreement/ECDHWithKdfBasicAgreement.cs b/src/core/srcbc/crypto/agreement/ECDHWithKdfBasicAgreement.cs new file mode 100644 index 0000000..0b0f3d2 --- /dev/null +++ b/src/core/srcbc/crypto/agreement/ECDHWithKdfBasicAgreement.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto.Agreement.Kdf; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + public class ECDHWithKdfBasicAgreement + : ECDHBasicAgreement + { + private static readonly Hashtable algorithms = new Hashtable(); + + static ECDHWithKdfBasicAgreement() + { + algorithms.Add(NistObjectIdentifiers.IdAes128Cbc.Id, 128); + algorithms.Add(NistObjectIdentifiers.IdAes192Cbc.Id, 192); + algorithms.Add(NistObjectIdentifiers.IdAes256Cbc.Id, 256); + algorithms.Add(NistObjectIdentifiers.IdAes128Wrap.Id, 128); + algorithms.Add(NistObjectIdentifiers.IdAes192Wrap.Id, 192); + algorithms.Add(NistObjectIdentifiers.IdAes256Wrap.Id, 256); + algorithms.Add(PkcsObjectIdentifiers.IdAlgCms3DesWrap.Id, 192); + } + + private readonly string algorithm; + private readonly IDerivationFunction kdf; + + public ECDHWithKdfBasicAgreement( + string algorithm, + IDerivationFunction kdf) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (!algorithms.Contains(algorithm)) + throw new ArgumentException("Unknown algorithm", "algorithm"); + if (kdf == null) + throw new ArgumentNullException("kdf"); + + this.algorithm = algorithm; + this.kdf = kdf; + } + + public override BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + BigInteger result = base.CalculateAgreement(pubKey); + + int keySize = (int) algorithms[algorithm]; + + DHKdfParameters dhKdfParams = new DHKdfParameters( + new DerObjectIdentifier(algorithm), + keySize, + // TODO Fix the way bytes are derived from the secret + result.ToByteArrayUnsigned()); + + kdf.Init(dhKdfParams); + + byte[] keyBytes = new byte[keySize / 8]; + kdf.GenerateBytes(keyBytes, 0, keyBytes.Length); + + return new BigInteger(1, keyBytes); + } + } +} diff --git a/src/core/srcbc/crypto/agreement/kdf/DHKdfParameters.cs b/src/core/srcbc/crypto/agreement/kdf/DHKdfParameters.cs new file mode 100644 index 0000000..eee337e --- /dev/null +++ b/src/core/srcbc/crypto/agreement/kdf/DHKdfParameters.cs @@ -0,0 +1,57 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Crypto.Agreement.Kdf +{ + public class DHKdfParameters + : IDerivationParameters + { + private readonly DerObjectIdentifier algorithm; + private readonly int keySize; + private readonly byte[] z; + private readonly byte[] extraInfo; + + public DHKdfParameters( + DerObjectIdentifier algorithm, + int keySize, + byte[] z) + : this(algorithm, keySize, z, null) + { + } + + public DHKdfParameters( + DerObjectIdentifier algorithm, + int keySize, + byte[] z, + byte[] extraInfo) + { + this.algorithm = algorithm; + this.keySize = keySize; + this.z = z; // TODO Clone? + this.extraInfo = extraInfo; + } + + public DerObjectIdentifier Algorithm + { + get { return algorithm; } + } + + public int KeySize + { + get { return keySize; } + } + + public byte[] GetZ() + { + // TODO Clone? + return z; + } + + public byte[] GetExtraInfo() + { + // TODO Clone? + return extraInfo; + } + } +} diff --git a/src/core/srcbc/crypto/agreement/kdf/DHKekGenerator.cs b/src/core/srcbc/crypto/agreement/kdf/DHKekGenerator.cs new file mode 100644 index 0000000..e2a00ad --- /dev/null +++ b/src/core/srcbc/crypto/agreement/kdf/DHKekGenerator.cs @@ -0,0 +1,129 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Crypto.Agreement.Kdf +{ + /** + * RFC 2631 Diffie-hellman KEK derivation function. + */ + public class DHKekGenerator + : IDerivationFunction + { + private readonly IDigest digest; + + private DerObjectIdentifier algorithm; + private int keySize; + private byte[] z; + private byte[] partyAInfo; + + public DHKekGenerator( + IDigest digest) + { + this.digest = digest; + } + + public void Init( + IDerivationParameters param) + { + DHKdfParameters parameters = (DHKdfParameters)param; + + this.algorithm = parameters.Algorithm; + this.keySize = parameters.KeySize; + this.z = parameters.GetZ(); // TODO Clone? + this.partyAInfo = parameters.GetExtraInfo(); // TODO Clone? + } + + public IDigest Digest + { + get { return digest; } + } + + public int GenerateBytes( + byte[] outBytes, + int outOff, + int len) + { + if ((outBytes.Length - len) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + long oBytes = len; + int outLen = digest.GetDigestSize(); + + // + // this is at odds with the standard implementation, the + // maximum value should be hBits * (2^32 - 1) where hBits + // is the digest output size in bits. We can't have an + // array with a long index at the moment... + // + if (oBytes > ((2L << 32) - 1)) + { + throw new ArgumentException("Output length too large"); + } + + int cThreshold = (int)((oBytes + outLen - 1) / outLen); + + byte[] dig = new byte[digest.GetDigestSize()]; + + int counter = 1; + + for (int i = 0; i < cThreshold; i++) + { + digest.BlockUpdate(z, 0, z.Length); + + // KeySpecificInfo + DerSequence keyInfo = new DerSequence( + algorithm, + new DerOctetString(integerToBytes(counter))); + + // OtherInfo + Asn1EncodableVector v1 = new Asn1EncodableVector(keyInfo); + + if (partyAInfo != null) + { + v1.Add(new DerTaggedObject(true, 0, new DerOctetString(partyAInfo))); + } + + v1.Add(new DerTaggedObject(true, 2, new DerOctetString(integerToBytes(keySize)))); + + byte[] other = new DerSequence(v1).GetDerEncoded(); + + digest.BlockUpdate(other, 0, other.Length); + + digest.DoFinal(dig, 0); + + if (len > outLen) + { + Array.Copy(dig, 0, outBytes, outOff, outLen); + outOff += outLen; + len -= outLen; + } + else + { + Array.Copy(dig, 0, outBytes, outOff, len); + } + + counter++; + } + + digest.Reset(); + + return len; + } + + private byte[] integerToBytes( + int keySize) + { + byte[] val = new byte[4]; + + val[0] = (byte)(keySize >> 24); + val[1] = (byte)(keySize >> 16); + val[2] = (byte)(keySize >> 8); + val[3] = (byte)keySize; + + return val; + } + } +} diff --git a/src/core/srcbc/crypto/agreement/kdf/ECDHKekGenerator.cs b/src/core/srcbc/crypto/agreement/kdf/ECDHKekGenerator.cs new file mode 100644 index 0000000..810153e --- /dev/null +++ b/src/core/srcbc/crypto/agreement/kdf/ECDHKekGenerator.cs @@ -0,0 +1,70 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Agreement.Kdf +{ + /** + * X9.63 based key derivation function for ECDH CMS. + */ + public class ECDHKekGenerator + : IDerivationFunction + { + private readonly IDerivationFunction kdf; + + private DerObjectIdentifier algorithm; + private int keySize; + private byte[] z; + + public ECDHKekGenerator( + IDigest digest) + { + this.kdf = new Kdf2BytesGenerator(digest); + } + + public void Init( + IDerivationParameters param) + { + DHKdfParameters parameters = (DHKdfParameters)param; + + this.algorithm = parameters.Algorithm; + this.keySize = parameters.KeySize; + this.z = parameters.GetZ(); // TODO Clone? + } + + public IDigest Digest + { + get { return kdf.Digest; } + } + + public int GenerateBytes( + byte[] outBytes, + int outOff, + int len) + { + // ECC-CMS-SharedInfo + DerSequence s = new DerSequence( + new AlgorithmIdentifier(algorithm, DerNull.Instance), + new DerTaggedObject(true, 2, new DerOctetString(integerToBytes(keySize)))); + + kdf.Init(new KdfParameters(z, s.GetDerEncoded())); + + return kdf.GenerateBytes(outBytes, outOff, len); + } + + private byte[] integerToBytes(int keySize) + { + byte[] val = new byte[4]; + + val[0] = (byte)(keySize >> 24); + val[1] = (byte)(keySize >> 16); + val[2] = (byte)(keySize >> 8); + val[3] = (byte)keySize; + + return val; + } + } +} diff --git a/src/core/srcbc/crypto/digests/GOST3411Digest.cs b/src/core/srcbc/crypto/digests/GOST3411Digest.cs new file mode 100644 index 0000000..9326d43 --- /dev/null +++ b/src/core/srcbc/crypto/digests/GOST3411Digest.cs @@ -0,0 +1,338 @@ +using System; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of GOST R 34.11-94 + */ + public class Gost3411Digest + : IDigest + { + private const int DIGEST_LENGTH = 32; + + private byte[] H = new byte[32], L = new byte[32], + M = new byte[32], Sum = new byte[32]; + private byte[][] C = new byte[4][]; + + private byte[] xBuf = new byte[32]; + private int xBufOff; + private long byteCount; + + private readonly IBlockCipher cipher = new Gost28147Engine(); + + /** + * Standard constructor + */ + public Gost3411Digest() + { + // TODO Is it possible to declare multi-dimensional arrays as in Java? + for (int i = 0; i < 4; ++i) + { + C[i] = new byte[32]; + } + + cipher.Init(true, new ParametersWithSBox(null, Gost28147Engine.GetSBox("D-A"))); + + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Gost3411Digest(Gost3411Digest t) + : this() + { +// cipher.Init(true, new ParametersWithSBox(null, Gost28147Engine.GetSBox("D-A"))); +// +// Reset(); + + Array.Copy(t.H, 0, this.H, 0, t.H.Length); + Array.Copy(t.L, 0, this.L, 0, t.L.Length); + Array.Copy(t.M, 0, this.M, 0, t.M.Length); + Array.Copy(t.Sum, 0, this.Sum, 0, t.Sum.Length); + Array.Copy(t.C[1], 0, this.C[1], 0, t.C[1].Length); + Array.Copy(t.C[2], 0, this.C[2], 0, t.C[2].Length); + Array.Copy(t.C[3], 0, this.C[3], 0, t.C[3].Length); + Array.Copy(t.xBuf, 0, this.xBuf, 0, t.xBuf.Length); + + this.xBufOff = t.xBufOff; + this.byteCount = t.byteCount; + } + + public string AlgorithmName + { + get { return "Gost3411"; } + } + + public int GetDigestSize() + { + return DIGEST_LENGTH; + } + + public void Update( + byte input) + { + xBuf[xBufOff++] = input; + if (xBufOff == xBuf.Length) + { + sumByteArray(xBuf); // calc sum M + processBlock(xBuf, 0); + xBufOff = 0; + } + byteCount++; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + while ((xBufOff != 0) && (length > 0)) + { + Update(input[inOff]); + inOff++; + length--; + } + + while (length > xBuf.Length) + { + Array.Copy(input, inOff, xBuf, 0, xBuf.Length); + + sumByteArray(xBuf); // calc sum M + processBlock(xBuf, 0); + inOff += xBuf.Length; + length -= xBuf.Length; + byteCount += xBuf.Length; + } + + // load in the remainder. + while (length > 0) + { + Update(input[inOff]); + inOff++; + length--; + } + } + + // (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8 + private byte[] K = new byte[32]; + + private byte[] P(byte[] input) + { + int fourK = 0; + for(int k = 0; k < 8; k++) + { + K[fourK++] = input[k]; + K[fourK++] = input[8 + k]; + K[fourK++] = input[16 + k]; + K[fourK++] = input[24 + k]; + } + + return K; + } + + //A (x) = (x0 ^ x1) || x3 || x2 || x1 + byte[] a = new byte[8]; + private byte[] A(byte[] input) + { + for(int j=0; j<8; j++) + { + a[j]=(byte)(input[j] ^ input[j+8]); + } + + Array.Copy(input, 8, input, 0, 24); + Array.Copy(a, 0, input, 24, 8); + + return input; + } + + //Encrypt function, ECB mode + private void E(byte[] key, byte[] s, int sOff, byte[] input, int inOff) + { + cipher.Init(true, new KeyParameter(key)); + + cipher.ProcessBlock(input, inOff, s, sOff); + } + + // (in:) n16||..||n1 ==> (out:) n1^n2^n3^n4^n13^n16||n16||..||n2 + internal short[] wS = new short[16], w_S = new short[16]; + + private void fw(byte[] input) + { + cpyBytesToShort(input, wS); + w_S[15] = (short)(wS[0] ^ wS[1] ^ wS[2] ^ wS[3] ^ wS[12] ^ wS[15]); + Array.Copy(wS, 1, w_S, 0, 15); + cpyShortToBytes(w_S, input); + } + + // block processing + internal byte[] S = new byte[32], U = new byte[32], V = new byte[32], W = new byte[32]; + + private void processBlock(byte[] input, int inOff) + { + Array.Copy(input, inOff, M, 0, 32); + + //key step 1 + + // H = h3 || h2 || h1 || h0 + // S = s3 || s2 || s1 || s0 + H.CopyTo(U, 0); + M.CopyTo(V, 0); + for (int j=0; j<32; j++) + { + W[j] = (byte)(U[j]^V[j]); + } + // Encrypt gost28147-ECB + E(P(W), S, 0, H, 0); // s0 = EK0 [h0] + + //keys step 2,3,4 + for (int i=1; i<4; i++) + { + byte[] tmpA = A(U); + for (int j=0; j<32; j++) + { + U[j] = (byte)(tmpA[j] ^ C[i][j]); + } + V = A(A(V)); + for (int j=0; j<32; j++) + { + W[j] = (byte)(U[j]^V[j]); + } + // Encrypt gost28147-ECB + E(P(W), S, i * 8, H, i * 8); // si = EKi [hi] + } + + // x(M, H) = y61(H^y(M^y12(S))) + for(int n = 0; n < 12; n++) + { + fw(S); + } + for(int n = 0; n < 32; n++) + { + S[n] = (byte)(S[n] ^ M[n]); + } + + fw(S); + + for(int n = 0; n < 32; n++) + { + S[n] = (byte)(H[n] ^ S[n]); + } + for(int n = 0; n < 61; n++) + { + fw(S); + } + Array.Copy(S, 0, H, 0, H.Length); + } + + private void finish() + { + LongToBytes(byteCount * 8, L, 0); // get length into L (byteCount * 8 = bitCount) + + while (xBufOff != 0) + { + Update((byte)0); + } + + processBlock(L, 0); + processBlock(Sum, 0); + } + + public int DoFinal( + byte[] output, + int outOff) + { + finish(); + + H.CopyTo(output, outOff); + + Reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + private static readonly byte[] C2 = { + 0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF, + (byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00,(byte)0xFF,0x00, + 0x00,(byte)0xFF,(byte)0xFF,0x00,(byte)0xFF,0x00,0x00,(byte)0xFF, + (byte)0xFF,0x00,0x00,0x00,(byte)0xFF,(byte)0xFF,0x00,(byte)0xFF + }; + + public void Reset() + { + byteCount = 0; + xBufOff = 0; + + Array.Clear(H, 0, H.Length); + Array.Clear(L, 0, L.Length); + Array.Clear(M, 0, M.Length); + Array.Clear(C[1], 0, C[1].Length); // real index C = +1 because index array with 0. + Array.Clear(C[3], 0, C[3].Length); + Array.Clear(Sum, 0, Sum.Length); + Array.Clear(xBuf, 0, xBuf.Length); + + C2.CopyTo(C[2], 0); + } + + // 256 bitsblock modul -> (Sum + a mod (2^256)) + private void sumByteArray( + byte[] input) + { + int carry = 0; + + for (int i = 0; i != Sum.Length; i++) + { + int sum = (Sum[i] & 0xff) + (input[i] & 0xff) + carry; + + Sum[i] = (byte)sum; + + carry = sum >> 8; + } + } + + // TODO Refactor as utility function + private static void LongToBytes( + long r, + byte[] output, + int outOff) + { + output[outOff + 7] = (byte)(r >> 56); + output[outOff + 6] = (byte)(r >> 48); + output[outOff + 5] = (byte)(r >> 40); + output[outOff + 4] = (byte)(r >> 32); + output[outOff + 3] = (byte)(r >> 24); + output[outOff + 2] = (byte)(r >> 16); + output[outOff + 1] = (byte)(r >> 8); + output[outOff] = (byte)r; + } + + private static void cpyBytesToShort(byte[] S, short[] wS) + { + for(int i = 0; i < S.Length / 2; i++) + { + wS[i] = (short)(((S[i*2+1]<<8)&0xFF00)|(S[i*2]&0xFF)); + } + } + + private static void cpyShortToBytes(short[] wS, byte[] S) + { + for(int i=0; i> 8); + S[i*2] = (byte)wS[i]; + } + } + + public int GetByteLength() + { + return 32; + } + } +} diff --git a/src/core/srcbc/crypto/digests/GeneralDigest.cs b/src/core/srcbc/crypto/digests/GeneralDigest.cs new file mode 100644 index 0000000..89f76f6 --- /dev/null +++ b/src/core/srcbc/crypto/digests/GeneralDigest.cs @@ -0,0 +1,118 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * base implementation of MD4 family style digest as outlined in + * "Handbook of Applied Cryptography", pages 344 - 347. + */ + public abstract class GeneralDigest + : IDigest + { + private const int BYTE_LENGTH = 64; + + private byte[] xBuf; + private int xBufOff; + + private long byteCount; + + internal GeneralDigest() + { + xBuf = new byte[4]; + } + + internal GeneralDigest(GeneralDigest t) + { + xBuf = new byte[t.xBuf.Length]; + Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + } + + public void Update(byte input) + { + xBuf[xBufOff++] = input; + + if (xBufOff == xBuf.Length) + { + ProcessWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + // + // fill the current word + // + while ((xBufOff != 0) && (length > 0)) + { + Update(input[inOff]); + inOff++; + length--; + } + + // + // process whole words. + // + while (length > xBuf.Length) + { + ProcessWord(input, inOff); + + inOff += xBuf.Length; + length -= xBuf.Length; + byteCount += xBuf.Length; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } + + public void Finish() + { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + Update((byte)128); + + while (xBufOff != 0) Update((byte)0); + ProcessLength(bitLength); + ProcessBlock(); + } + + public virtual void Reset() + { + byteCount = 0; + xBufOff = 0; + for ( int i = 0; i < xBuf.Length; i++ ) xBuf[i] = 0; + } + + public int GetByteLength() + { + return BYTE_LENGTH; + } + + internal abstract void ProcessWord(byte[] input, int inOff); + internal abstract void ProcessLength(long bitLength); + internal abstract void ProcessBlock(); + public abstract string AlgorithmName { get; } + public abstract int GetDigestSize(); + public abstract int DoFinal(byte[] output, int outOff); + } +} diff --git a/src/core/srcbc/crypto/digests/LongDigest.cs b/src/core/srcbc/crypto/digests/LongDigest.cs new file mode 100644 index 0000000..8ff6eb0 --- /dev/null +++ b/src/core/srcbc/crypto/digests/LongDigest.cs @@ -0,0 +1,380 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Base class for SHA-384 and SHA-512. + */ + public abstract class LongDigest + : IDigest + { + private int MyByteLength = 128; + + private byte[] xBuf; + private int xBufOff; + + private long byteCount1; + private long byteCount2; + + internal long H1, H2, H3, H4, H5, H6, H7, H8; + + private long[] W = new long[80]; + private int wOff; + + /** + * Constructor for variable length word + */ + internal LongDigest() + { + xBuf = new byte[8]; + + Reset(); + } + + /** + * Copy constructor. We are using copy constructors in place + * of the object.Clone() interface as this interface is not + * supported by J2ME. + */ + internal LongDigest( + LongDigest t) + { + xBuf = new byte[t.xBuf.Length]; + Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); + + xBufOff = t.xBufOff; + byteCount1 = t.byteCount1; + byteCount2 = t.byteCount2; + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + Array.Copy(t.W, 0, W, 0, t.W.Length); + wOff = t.wOff; + } + + public void Update( + byte input) + { + xBuf[xBufOff++] = input; + + if (xBufOff == xBuf.Length) + { + ProcessWord(xBuf, 0); + xBufOff = 0; + } + + byteCount1++; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + // + // fill the current word + // + while ((xBufOff != 0) && (length > 0)) + { + Update(input[inOff]); + + inOff++; + length--; + } + + // + // process whole words. + // + while (length > xBuf.Length) + { + ProcessWord(input, inOff); + + inOff += xBuf.Length; + length -= xBuf.Length; + byteCount1 += xBuf.Length; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } + + public void Finish() + { + AdjustByteCounts(); + + long lowBitLength = byteCount1 << 3; + long hiBitLength = byteCount2; + + // + // add the pad bytes. + // + Update((byte)128); + + while (xBufOff != 0) + { + Update((byte)0); + } + + ProcessLength(lowBitLength, hiBitLength); + + ProcessBlock(); + } + + public virtual void Reset() + { + byteCount1 = 0; + byteCount2 = 0; + + xBufOff = 0; + for ( int i = 0; i < xBuf.Length; i++ ) + { + xBuf[i] = 0; + } + + wOff = 0; + for (int i = 0; i != W.Length; i++) + { + W[i] = 0; + } + } + + internal void ProcessWord( + byte[] input, + int inOff) + { + W[wOff++] = ((long)(input[inOff] & 0xff) << 56) + | ((long)(input[inOff + 1] & 0xff) << 48) + | ((long)(input[inOff + 2] & 0xff) << 40) + | ((long)(input[inOff + 3] & 0xff) << 32) + | ((long)(input[inOff + 4] & 0xff) << 24) + | ((long)(input[inOff + 5] & 0xff) << 16) + | ((long)(input[inOff + 6] & 0xff) << 8) + | ((uint)(input[inOff + 7] & 0xff) ); + + if (wOff == 16) + { + ProcessBlock(); + } + } + + internal static void UnpackWord( + long word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)((ulong) word >> 56); + outBytes[outOff + 1] = (byte)((ulong) word >> 48); + outBytes[outOff + 2] = (byte)((ulong) word >> 40); + outBytes[outOff + 3] = (byte)((ulong) word >> 32); + outBytes[outOff + 4] = (byte)((ulong) word >> 24); + outBytes[outOff + 5] = (byte)((ulong) word >> 16); + outBytes[outOff + 6] = (byte)((ulong) word >> 8); + outBytes[outOff + 7] = (byte)word; + } + + /** + * adjust the byte counts so that byteCount2 represents the + * upper long (less 3 bits) word of the byte count. + */ + private void AdjustByteCounts() + { + if (byteCount1 > 0x1fffffffffffffffL) + { + byteCount2 += (long) ((ulong) byteCount1 >> 61); + byteCount1 &= 0x1fffffffffffffffL; + } + } + + internal void ProcessLength( + long lowW, + long hiW) + { + if (wOff > 14) + { + ProcessBlock(); + } + + W[14] = hiW; + W[15] = lowW; + } + + internal void ProcessBlock() + { + AdjustByteCounts(); + + // + // expand 16 word block into 80 word blocks. + // + for (int ti = 16; ti <= 79; ++ti) + { + W[ti] = Sigma1(W[ti - 2]) + W[ti - 7] + Sigma0(W[ti - 15]) + W[ti - 16]; + } + + // + // set up working variables. + // + long a = H1; + long b = H2; + long c = H3; + long d = H4; + long e = H5; + long f = H6; + long g = H7; + long h = H8; + + int t = 0; + for(int i = 0; i < 10; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++]; + d += h; + h += Sum0(a) + Maj(a, b, c); + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++]; + b += f; + f += Sum0(g) + Maj(g, h, a); + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++]; + h += d; + d += Sum0(e) + Maj(e, f, g); + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++]; + f += b; + b += Sum0(c) + Maj(c, d, e); + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++]; + e += a; + a += Sum0(b) + Maj(b, c, d); + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + wOff = 0; + + Array.Clear(W, 0, 16); + } + + /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ + private static long Ch( + long x, + long y, + long z) + { + return ((x & y) ^ ((~x) & z)); + } + + private static long Maj( + long x, + long y, + long z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private static long Sum0( + long x) + { + return ((x << 36)|((long)((ulong)x >> 28))) ^ ((x << 30)|((long)((ulong)x >> 34))) ^ ((x << 25)|((long)((ulong)x >> 39))); + } + + private static long Sum1( + long x) + { + return ((x << 50)|((long)((ulong)x >> 14))) ^ ((x << 46)|((long)((ulong)x >> 18))) ^ ((x << 23)|((long)((ulong)x >> 41))); + } + + private static long Sigma0( + long x) + { + return ((x << 63)|((long)((ulong)x >> 1))) ^ ((x << 56)|((long)((ulong)x >> 8))) ^ ((long)((ulong)x >> 7)); + } + + private static long Sigma1( + long x) + { + return ((x << 45)|((long)((ulong)x >> 19))) ^ ((x << 3)|((long)((ulong)x >> 61))) ^ ((long)((ulong)x >> 6)); + } + + /* SHA-384 and SHA-512 Constants + * (represent the first 64 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + internal static readonly long[] K = + { + unchecked((long) 0x428a2f98d728ae22L), unchecked((long) 0x7137449123ef65cdL), unchecked((long) 0xb5c0fbcfec4d3b2fL), unchecked((long) 0xe9b5dba58189dbbcL), + unchecked((long) 0x3956c25bf348b538L), unchecked((long) 0x59f111f1b605d019L), unchecked((long) 0x923f82a4af194f9bL), unchecked((long) 0xab1c5ed5da6d8118L), + unchecked((long) 0xd807aa98a3030242L), unchecked((long) 0x12835b0145706fbeL), unchecked((long) 0x243185be4ee4b28cL), unchecked((long) 0x550c7dc3d5ffb4e2L), + unchecked((long) 0x72be5d74f27b896fL), unchecked((long) 0x80deb1fe3b1696b1L), unchecked((long) 0x9bdc06a725c71235L), unchecked((long) 0xc19bf174cf692694L), + unchecked((long) 0xe49b69c19ef14ad2L), unchecked((long) 0xefbe4786384f25e3L), unchecked((long) 0x0fc19dc68b8cd5b5L), unchecked((long) 0x240ca1cc77ac9c65L), + unchecked((long) 0x2de92c6f592b0275L), unchecked((long) 0x4a7484aa6ea6e483L), unchecked((long) 0x5cb0a9dcbd41fbd4L), unchecked((long) 0x76f988da831153b5L), + unchecked((long) 0x983e5152ee66dfabL), unchecked((long) 0xa831c66d2db43210L), unchecked((long) 0xb00327c898fb213fL), unchecked((long) 0xbf597fc7beef0ee4L), + unchecked((long) 0xc6e00bf33da88fc2L), unchecked((long) 0xd5a79147930aa725L), unchecked((long) 0x06ca6351e003826fL), unchecked((long) 0x142929670a0e6e70L), + unchecked((long) 0x27b70a8546d22ffcL), unchecked((long) 0x2e1b21385c26c926L), unchecked((long) 0x4d2c6dfc5ac42aedL), unchecked((long) 0x53380d139d95b3dfL), + unchecked((long) 0x650a73548baf63deL), unchecked((long) 0x766a0abb3c77b2a8L), unchecked((long) 0x81c2c92e47edaee6L), unchecked((long) 0x92722c851482353bL), + unchecked((long) 0xa2bfe8a14cf10364L), unchecked((long) 0xa81a664bbc423001L), unchecked((long) 0xc24b8b70d0f89791L), unchecked((long) 0xc76c51a30654be30L), + unchecked((long) 0xd192e819d6ef5218L), unchecked((long) 0xd69906245565a910L), unchecked((long) 0xf40e35855771202aL), unchecked((long) 0x106aa07032bbd1b8L), + unchecked((long) 0x19a4c116b8d2d0c8L), unchecked((long) 0x1e376c085141ab53L), unchecked((long) 0x2748774cdf8eeb99L), unchecked((long) 0x34b0bcb5e19b48a8L), + unchecked((long) 0x391c0cb3c5c95a63L), unchecked((long) 0x4ed8aa4ae3418acbL), unchecked((long) 0x5b9cca4f7763e373L), unchecked((long) 0x682e6ff3d6b2b8a3L), + unchecked((long) 0x748f82ee5defb2fcL), unchecked((long) 0x78a5636f43172f60L), unchecked((long) 0x84c87814a1f0ab72L), unchecked((long) 0x8cc702081a6439ecL), + unchecked((long) 0x90befffa23631e28L), unchecked((long) 0xa4506cebde82bde9L), unchecked((long) 0xbef9a3f7b2c67915L), unchecked((long) 0xc67178f2e372532bL), + unchecked((long) 0xca273eceea26619cL), unchecked((long) 0xd186b8c721c0c207L), unchecked((long) 0xeada7dd6cde0eb1eL), unchecked((long) 0xf57d4f7fee6ed178L), + unchecked((long) 0x06f067aa72176fbaL), unchecked((long) 0x0a637dc5a2c898a6L), unchecked((long) 0x113f9804bef90daeL), unchecked((long) 0x1b710b35131c471bL), + unchecked((long) 0x28db77f523047d84L), unchecked((long) 0x32caab7b40c72493L), unchecked((long) 0x3c9ebe0a15c9bebcL), unchecked((long) 0x431d67c49c100d4cL), + unchecked((long) 0x4cc5d4becb3e42b6L), unchecked((long) 0x597f299cfc657e2aL), unchecked((long) 0x5fcb6fab3ad6faecL), unchecked((long) 0x6c44198c4a475817L) + }; + + public int GetByteLength() + { + return MyByteLength; + } + + public abstract string AlgorithmName { get; } + public abstract int GetDigestSize(); + public abstract int DoFinal(byte[] output, int outOff); + } +} diff --git a/src/core/srcbc/crypto/digests/MD2Digest.cs b/src/core/srcbc/crypto/digests/MD2Digest.cs new file mode 100644 index 0000000..2842680 --- /dev/null +++ b/src/core/srcbc/crypto/digests/MD2Digest.cs @@ -0,0 +1,247 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /** + * implementation of MD2 + * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992 + */ + public class MD2Digest + : IDigest + { + private const int DigestLength = 16; + private const int BYTE_LENGTH = 16; + + /* X buffer */ + private byte[] X = new byte[48]; + private int xOff; + + /* M buffer */ + + private byte[] M = new byte[16]; + private int mOff; + + /* check sum */ + + private byte[] C = new byte[16]; + private int COff; + + public MD2Digest() + { + Reset(); + } + public MD2Digest(MD2Digest t) + { + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + Array.Copy(t.M, 0, M, 0, t.M.Length); + mOff = t.mOff; + Array.Copy(t.C, 0, C, 0, t.C.Length); + COff = t.COff; + } + /** + * return the algorithm name + * + * @return the algorithm name + */ + public string AlgorithmName + { + get { return "MD2"; } + } + + public int GetDigestSize() + { + return DigestLength; + } + + public int GetByteLength() + { + return BYTE_LENGTH; + } + + /** + * Close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param out the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + public int DoFinal(byte[] output, int outOff) + { + // add padding + byte paddingByte = (byte)(M.Length - mOff); + for (int i=mOff;i 0)) + { + Update(input[inOff]); + inOff++; + length--; + } + + // + // process whole words. + // + while (length > 16) + { + Array.Copy(input,inOff,M,0,16); + ProcessChecksum(M); + ProcessBlock(M); + length -= 16; + inOff += 16; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + inOff++; + length--; + } + } + + internal void ProcessChecksum(byte[] m) + { + int L = C[15]; + for (int i=0;i<16;i++) + { + C[i] ^= S[(m[i] ^ L) & 0xff]; + L = C[i]; + } + } + internal void ProcessBlock(byte[] m) + { + for (int i=0;i<16;i++) + { + X[i+16] = m[i]; + X[i+32] = (byte)(m[i] ^ X[i]); + } + // encrypt block + int t = 0; + + for (int j=0;j<18;j++) + { + for (int k=0;k<48;k++) + { + t = X[k] ^= S[t]; + t = t & 0xff; + } + t = (t + j)%256; + } + } + + + + // 256-byte random permutation constructed from the digits of PI + private static readonly byte[] S = { + (byte)41,(byte)46,(byte)67,(byte)201,(byte)162,(byte)216,(byte)124, + (byte)1,(byte)61,(byte)54,(byte)84,(byte)161,(byte)236,(byte)240, + (byte)6,(byte)19,(byte)98,(byte)167,(byte)5,(byte)243,(byte)192, + (byte)199,(byte)115,(byte)140,(byte)152,(byte)147,(byte)43,(byte)217, + (byte)188,(byte)76,(byte)130,(byte)202,(byte)30,(byte)155,(byte)87, + (byte)60,(byte)253,(byte)212,(byte)224,(byte)22,(byte)103,(byte)66, + (byte)111,(byte)24,(byte)138,(byte)23,(byte)229,(byte)18,(byte)190, + (byte)78,(byte)196,(byte)214,(byte)218,(byte)158,(byte)222,(byte)73, + (byte)160,(byte)251,(byte)245,(byte)142,(byte)187,(byte)47,(byte)238, + (byte)122,(byte)169,(byte)104,(byte)121,(byte)145,(byte)21,(byte)178, + (byte)7,(byte)63,(byte)148,(byte)194,(byte)16,(byte)137,(byte)11, + (byte)34,(byte)95,(byte)33,(byte)128,(byte)127,(byte)93,(byte)154, + (byte)90,(byte)144,(byte)50,(byte)39,(byte)53,(byte)62,(byte)204, + (byte)231,(byte)191,(byte)247,(byte)151,(byte)3,(byte)255,(byte)25, + (byte)48,(byte)179,(byte)72,(byte)165,(byte)181,(byte)209,(byte)215, + (byte)94,(byte)146,(byte)42,(byte)172,(byte)86,(byte)170,(byte)198, + (byte)79,(byte)184,(byte)56,(byte)210,(byte)150,(byte)164,(byte)125, + (byte)182,(byte)118,(byte)252,(byte)107,(byte)226,(byte)156,(byte)116, + (byte)4,(byte)241,(byte)69,(byte)157,(byte)112,(byte)89,(byte)100, + (byte)113,(byte)135,(byte)32,(byte)134,(byte)91,(byte)207,(byte)101, + (byte)230,(byte)45,(byte)168,(byte)2,(byte)27,(byte)96,(byte)37, + (byte)173,(byte)174,(byte)176,(byte)185,(byte)246,(byte)28,(byte)70, + (byte)97,(byte)105,(byte)52,(byte)64,(byte)126,(byte)15,(byte)85, + (byte)71,(byte)163,(byte)35,(byte)221,(byte)81,(byte)175,(byte)58, + (byte)195,(byte)92,(byte)249,(byte)206,(byte)186,(byte)197,(byte)234, + (byte)38,(byte)44,(byte)83,(byte)13,(byte)110,(byte)133,(byte)40, + (byte)132, 9,(byte)211,(byte)223,(byte)205,(byte)244,(byte)65, + (byte)129,(byte)77,(byte)82,(byte)106,(byte)220,(byte)55,(byte)200, + (byte)108,(byte)193,(byte)171,(byte)250,(byte)36,(byte)225,(byte)123, + (byte)8,(byte)12,(byte)189,(byte)177,(byte)74,(byte)120,(byte)136, + (byte)149,(byte)139,(byte)227,(byte)99,(byte)232,(byte)109,(byte)233, + (byte)203,(byte)213,(byte)254,(byte)59,(byte)0,(byte)29,(byte)57, + (byte)242,(byte)239,(byte)183,(byte)14,(byte)102,(byte)88,(byte)208, + (byte)228,(byte)166,(byte)119,(byte)114,(byte)248,(byte)235,(byte)117, + (byte)75,(byte)10,(byte)49,(byte)68,(byte)80,(byte)180,(byte)143, + (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51, + (byte)159,(byte)17,(byte)131,(byte)20 + }; + } + +} diff --git a/src/core/srcbc/crypto/digests/MD4Digest.cs b/src/core/srcbc/crypto/digests/MD4Digest.cs new file mode 100644 index 0000000..1494f04 --- /dev/null +++ b/src/core/srcbc/crypto/digests/MD4Digest.cs @@ -0,0 +1,271 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of MD4 as RFC 1320 by R. Rivest, MIT Laboratory for + * Computer Science and RSA Data Security, Inc. + *

+ * NOTE: This algorithm is only included for backwards compatibility + * with legacy applications, it's not secure, don't use it for anything new!

+ */ + public class MD4Digest + : GeneralDigest + { + private const int DigestLength = 16; + + private int H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public MD4Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public MD4Digest(MD4Digest t) : base(t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "MD4"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong) bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint) word >> 8); + outBytes[outOff + 2] = (byte)((uint) word >> 16); + outBytes[outOff + 3] = (byte)((uint) word >> 24); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 4); + UnpackWord(H3, output, outOff + 8); + UnpackWord(H4, output, outOff + 12); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables to the IV values. + */ + public override void Reset() + { + base.Reset(); + + H1 = unchecked((int) 0x67452301); + H2 = unchecked((int) 0xefcdab89); + H3 = unchecked((int) 0x98badcfe); + H4 = unchecked((int) 0x10325476); + + xOff = 0; + + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private const int S11 = 3; + private const int S12 = 7; + private const int S13 = 11; + private const int S14 = 19; + + // + // round 2 left rotates + // + private const int S21 = 3; + private const int S22 = 5; + private const int S23 = 9; + private const int S24 = 13; + + // + // round 3 left rotates + // + private const int S31 = 3; + private const int S32 = 9; + private const int S33 = 11; + private const int S34 = 15; + + /* + * rotate int x left n bits. + */ + private int RotateLeft( + int x, + int n) + { + return (x << n) | (int) ((uint) x >> (32 - n)); + } + + /* + * F, G, H and I are the basic MD4 functions. + */ + private int F( + int u, + int v, + int w) + { + return (u & v) | (~u & w); + } + + private int G( + int u, + int v, + int w) + { + return (u & v) | (u & w) | (v & w); + } + + private int H( + int u, + int v, + int w) + { + return u ^ v ^ w; + } + + internal override void ProcessBlock() + { + int a = H1; + int b = H2; + int c = H3; + int d = H4; + + // + // Round 1 - F cycle, 16 times. + // + a = RotateLeft((a + F(b, c, d) + X[ 0]), S11); + d = RotateLeft((d + F(a, b, c) + X[ 1]), S12); + c = RotateLeft((c + F(d, a, b) + X[ 2]), S13); + b = RotateLeft((b + F(c, d, a) + X[ 3]), S14); + a = RotateLeft((a + F(b, c, d) + X[ 4]), S11); + d = RotateLeft((d + F(a, b, c) + X[ 5]), S12); + c = RotateLeft((c + F(d, a, b) + X[ 6]), S13); + b = RotateLeft((b + F(c, d, a) + X[ 7]), S14); + a = RotateLeft((a + F(b, c, d) + X[ 8]), S11); + d = RotateLeft((d + F(a, b, c) + X[ 9]), S12); + c = RotateLeft((c + F(d, a, b) + X[10]), S13); + b = RotateLeft((b + F(c, d, a) + X[11]), S14); + a = RotateLeft((a + F(b, c, d) + X[12]), S11); + d = RotateLeft((d + F(a, b, c) + X[13]), S12); + c = RotateLeft((c + F(d, a, b) + X[14]), S13); + b = RotateLeft((b + F(c, d, a) + X[15]), S14); + + // + // Round 2 - G cycle, 16 times. + // + a = RotateLeft((a + G(b, c, d) + X[ 0] + 0x5a827999), S21); + d = RotateLeft((d + G(a, b, c) + X[ 4] + 0x5a827999), S22); + c = RotateLeft((c + G(d, a, b) + X[ 8] + 0x5a827999), S23); + b = RotateLeft((b + G(c, d, a) + X[12] + 0x5a827999), S24); + a = RotateLeft((a + G(b, c, d) + X[ 1] + 0x5a827999), S21); + d = RotateLeft((d + G(a, b, c) + X[ 5] + 0x5a827999), S22); + c = RotateLeft((c + G(d, a, b) + X[ 9] + 0x5a827999), S23); + b = RotateLeft((b + G(c, d, a) + X[13] + 0x5a827999), S24); + a = RotateLeft((a + G(b, c, d) + X[ 2] + 0x5a827999), S21); + d = RotateLeft((d + G(a, b, c) + X[ 6] + 0x5a827999), S22); + c = RotateLeft((c + G(d, a, b) + X[10] + 0x5a827999), S23); + b = RotateLeft((b + G(c, d, a) + X[14] + 0x5a827999), S24); + a = RotateLeft((a + G(b, c, d) + X[ 3] + 0x5a827999), S21); + d = RotateLeft((d + G(a, b, c) + X[ 7] + 0x5a827999), S22); + c = RotateLeft((c + G(d, a, b) + X[11] + 0x5a827999), S23); + b = RotateLeft((b + G(c, d, a) + X[15] + 0x5a827999), S24); + + // + // Round 3 - H cycle, 16 times. + // + a = RotateLeft((a + H(b, c, d) + X[ 0] + 0x6ed9eba1), S31); + d = RotateLeft((d + H(a, b, c) + X[ 8] + 0x6ed9eba1), S32); + c = RotateLeft((c + H(d, a, b) + X[ 4] + 0x6ed9eba1), S33); + b = RotateLeft((b + H(c, d, a) + X[12] + 0x6ed9eba1), S34); + a = RotateLeft((a + H(b, c, d) + X[ 2] + 0x6ed9eba1), S31); + d = RotateLeft((d + H(a, b, c) + X[10] + 0x6ed9eba1), S32); + c = RotateLeft((c + H(d, a, b) + X[ 6] + 0x6ed9eba1), S33); + b = RotateLeft((b + H(c, d, a) + X[14] + 0x6ed9eba1), S34); + a = RotateLeft((a + H(b, c, d) + X[ 1] + 0x6ed9eba1), S31); + d = RotateLeft((d + H(a, b, c) + X[ 9] + 0x6ed9eba1), S32); + c = RotateLeft((c + H(d, a, b) + X[ 5] + 0x6ed9eba1), S33); + b = RotateLeft((b + H(c, d, a) + X[13] + 0x6ed9eba1), S34); + a = RotateLeft((a + H(b, c, d) + X[ 3] + 0x6ed9eba1), S31); + d = RotateLeft((d + H(a, b, c) + X[11] + 0x6ed9eba1), S32); + c = RotateLeft((c + H(d, a, b) + X[ 7] + 0x6ed9eba1), S33); + b = RotateLeft((b + H(c, d, a) + X[15] + 0x6ed9eba1), S34); + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + } + +} diff --git a/src/core/srcbc/crypto/digests/MD5Digest.cs b/src/core/srcbc/crypto/digests/MD5Digest.cs new file mode 100644 index 0000000..e9385d6 --- /dev/null +++ b/src/core/srcbc/crypto/digests/MD5Digest.cs @@ -0,0 +1,301 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. + */ + public class MD5Digest + : GeneralDigest + { + private const int DigestLength = 16; + + private int H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + public MD5Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public MD5Digest(MD5Digest t) + : base(t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "MD5"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong) bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint) word >> 8); + outBytes[outOff + 2] = (byte)((uint) word >> 16); + outBytes[outOff + 3] = (byte)((uint) word >> 24); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 4); + UnpackWord(H3, output, outOff + 8); + UnpackWord(H4, output, outOff + 12); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables to the IV values. + */ + public override void Reset() + { + base.Reset(); + + H1 = unchecked((int) 0x67452301); + H2 = unchecked((int) 0xefcdab89); + H3 = unchecked((int) 0x98badcfe); + H4 = unchecked((int) 0x10325476); + + xOff = 0; + + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private static readonly int S11 = 7; + private static readonly int S12 = 12; + private static readonly int S13 = 17; + private static readonly int S14 = 22; + + // + // round 2 left rotates + // + private static readonly int S21 = 5; + private static readonly int S22 = 9; + private static readonly int S23 = 14; + private static readonly int S24 = 20; + + // + // round 3 left rotates + // + private static readonly int S31 = 4; + private static readonly int S32 = 11; + private static readonly int S33 = 16; + private static readonly int S34 = 23; + + // + // round 4 left rotates + // + private static readonly int S41 = 6; + private static readonly int S42 = 10; + private static readonly int S43 = 15; + private static readonly int S44 = 21; + + /* + * rotate int x left n bits. + */ + private int RotateLeft( + int x, + int n) + { + return (x << n) | (int) ((uint) x >> (32 - n)); + } + + /* + * F, G, H and I are the basic MD5 functions. + */ + private int F( + int u, + int v, + int w) + { + return (u & v) | (~u & w); + } + + private int G( + int u, + int v, + int w) + { + return (u & w) | (v & ~w); + } + + private int H( + int u, + int v, + int w) + { + return u ^ v ^ w; + } + + private int K( + int u, + int v, + int w) + { + return v ^ (u | ~w); + } + + internal override void ProcessBlock() + { + int a = H1; + int b = H2; + int c = H3; + int d = H4; + + // + // Round 1 - F cycle, 16 times. + // + a = RotateLeft((a + F(b, c, d) + X[ 0] + unchecked((int) 0xd76aa478)), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[ 1] + unchecked((int) 0xe8c7b756)), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[ 2] + unchecked((int) 0x242070db)), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[ 3] + unchecked((int) 0xc1bdceee)), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[ 4] + unchecked((int) 0xf57c0faf)), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[ 5] + unchecked((int) 0x4787c62a)), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[ 6] + unchecked((int) 0xa8304613)), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[ 7] + unchecked((int) 0xfd469501)), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[ 8] + unchecked((int) 0x698098d8)), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[ 9] + unchecked((int) 0x8b44f7af)), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[10] + unchecked((int) 0xffff5bb1)), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[11] + unchecked((int) 0x895cd7be)), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[12] + unchecked((int) 0x6b901122)), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[13] + unchecked((int) 0xfd987193)), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[14] + unchecked((int) 0xa679438e)), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[15] + unchecked((int) 0x49b40821)), S14) + c; + + // + // Round 2 - G cycle, 16 times. + // + a = RotateLeft((a + G(b, c, d) + X[ 1] + unchecked((int) 0xf61e2562)), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[ 6] + unchecked((int) 0xc040b340)), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[11] + unchecked((int) 0x265e5a51)), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[ 0] + unchecked((int) 0xe9b6c7aa)), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[ 5] + unchecked((int) 0xd62f105d)), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[10] + unchecked((int) 0x02441453)), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[15] + unchecked((int) 0xd8a1e681)), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[ 4] + unchecked((int) 0xe7d3fbc8)), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[ 9] + unchecked((int) 0x21e1cde6)), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[14] + unchecked((int) 0xc33707d6)), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[ 3] + unchecked((int) 0xf4d50d87)), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[ 8] + unchecked((int) 0x455a14ed)), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[13] + unchecked((int) 0xa9e3e905)), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[ 2] + unchecked((int) 0xfcefa3f8)), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[ 7] + unchecked((int) 0x676f02d9)), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[12] + unchecked((int) 0x8d2a4c8a)), S24) + c; + + // + // Round 3 - H cycle, 16 times. + // + a = RotateLeft((a + H(b, c, d) + X[ 5] + unchecked((int) 0xfffa3942)), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[ 8] + unchecked((int) 0x8771f681)), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[11] + unchecked((int) 0x6d9d6122)), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[14] + unchecked((int) 0xfde5380c)), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[ 1] + unchecked((int) 0xa4beea44)), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[ 4] + unchecked((int) 0x4bdecfa9)), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[ 7] + unchecked((int) 0xf6bb4b60)), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[10] + unchecked((int) 0xbebfbc70)), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[13] + unchecked((int) 0x289b7ec6)), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[ 0] + unchecked((int) 0xeaa127fa)), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[ 3] + unchecked((int) 0xd4ef3085)), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[ 6] + unchecked((int) 0x04881d05)), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[ 9] + unchecked((int) 0xd9d4d039)), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[12] + unchecked((int) 0xe6db99e5)), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[15] + unchecked((int) 0x1fa27cf8)), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[ 2] + unchecked((int) 0xc4ac5665)), S34) + c; + + // + // Round 4 - K cycle, 16 times. + // + a = RotateLeft((a + K(b, c, d) + X[ 0] + unchecked((int) 0xf4292244)), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[ 7] + unchecked((int) 0x432aff97)), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[14] + unchecked((int) 0xab9423a7)), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[ 5] + unchecked((int) 0xfc93a039)), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[12] + unchecked((int) 0x655b59c3)), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[ 3] + unchecked((int) 0x8f0ccc92)), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[10] + unchecked((int) 0xffeff47d)), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[ 1] + unchecked((int) 0x85845dd1)), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[ 8] + unchecked((int) 0x6fa87e4f)), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[15] + unchecked((int) 0xfe2ce6e0)), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[ 6] + unchecked((int) 0xa3014314)), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[13] + unchecked((int) 0x4e0811a1)), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[ 4] + unchecked((int) 0xf7537e82)), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[11] + unchecked((int) 0xbd3af235)), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[ 2] + unchecked((int) 0x2ad7d2bb)), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[ 9] + unchecked((int) 0xeb86d391)), S44) + c; + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + } + +} diff --git a/src/core/srcbc/crypto/digests/RipeMD128Digest.cs b/src/core/srcbc/crypto/digests/RipeMD128Digest.cs new file mode 100644 index 0000000..c263796 --- /dev/null +++ b/src/core/srcbc/crypto/digests/RipeMD128Digest.cs @@ -0,0 +1,462 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of RipeMD128 + */ + public class RipeMD128Digest + : GeneralDigest + { + private const int DigestLength = 16; + + private int H0, H1, H2, H3; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public RipeMD128Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public RipeMD128Digest(RipeMD128Digest t) : base(t) + { + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "RIPEMD128"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong) bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint) word >> 8); + outBytes[outOff + 2] = (byte)((uint) word >> 16); + outBytes[outOff + 3] = (byte)((uint) word >> 24); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H0, output, outOff); + UnpackWord(H1, output, outOff + 4); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 12); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables to the IV values. + */ + public override void Reset() + { + base.Reset(); + + H0 = unchecked((int) 0x67452301); + H1 = unchecked((int) 0xefcdab89); + H2 = unchecked((int) 0x98badcfe); + H3 = unchecked((int) 0x10325476); + + xOff = 0; + + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (int) ((uint) x >> (32 - n)); + } + + /* + * f1,f2,f3,f4 are the basic RipeMD128 functions. + */ + + /* + * F + */ + private int F1( + int x, + int y, + int z) + { + return x ^ y ^ z; + } + + /* + * G + */ + private int F2( + int x, + int y, + int z) + { + return (x & y) | (~x & z); + } + + /* + * H + */ + private int F3( + int x, + int y, + int z) + { + return (x | ~y) ^ z; + } + + /* + * I + */ + private int F4( + int x, + int y, + int z) + { + return (x & z) | (y & ~z); + } + + private int F1( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + F1(b, c, d) + x, s); + } + + private int F2( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + F2(b, c, d) + x + unchecked((int) 0x5a827999), s); + } + + private int F3( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + F3(b, c, d) + x + unchecked((int) 0x6ed9eba1), s); + } + + private int F4( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + F4(b, c, d) + x + unchecked((int) 0x8f1bbcdc), s); + } + + private int FF1( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + F1(b, c, d) + x, s); + } + + private int FF2( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + F2(b, c, d) + x + unchecked((int) 0x6d703ef3), s); + } + + private int FF3( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + F3(b, c, d) + x + unchecked((int) 0x5c4dd124), s); + } + + private int FF4( + int a, + int b, + int c, + int d, + int x, + int s) + { + return RL(a + F4(b, c, d) + x + unchecked((int) 0x50a28be6), s); + } + + internal override void ProcessBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + + a = aa = H0; + b = bb = H1; + c = cc = H2; + d = dd = H3; + + // + // Round 1 + // + a = F1(a, b, c, d, X[ 0], 11); + d = F1(d, a, b, c, X[ 1], 14); + c = F1(c, d, a, b, X[ 2], 15); + b = F1(b, c, d, a, X[ 3], 12); + a = F1(a, b, c, d, X[ 4], 5); + d = F1(d, a, b, c, X[ 5], 8); + c = F1(c, d, a, b, X[ 6], 7); + b = F1(b, c, d, a, X[ 7], 9); + a = F1(a, b, c, d, X[ 8], 11); + d = F1(d, a, b, c, X[ 9], 13); + c = F1(c, d, a, b, X[10], 14); + b = F1(b, c, d, a, X[11], 15); + a = F1(a, b, c, d, X[12], 6); + d = F1(d, a, b, c, X[13], 7); + c = F1(c, d, a, b, X[14], 9); + b = F1(b, c, d, a, X[15], 8); + + // + // Round 2 + // + a = F2(a, b, c, d, X[ 7], 7); + d = F2(d, a, b, c, X[ 4], 6); + c = F2(c, d, a, b, X[13], 8); + b = F2(b, c, d, a, X[ 1], 13); + a = F2(a, b, c, d, X[10], 11); + d = F2(d, a, b, c, X[ 6], 9); + c = F2(c, d, a, b, X[15], 7); + b = F2(b, c, d, a, X[ 3], 15); + a = F2(a, b, c, d, X[12], 7); + d = F2(d, a, b, c, X[ 0], 12); + c = F2(c, d, a, b, X[ 9], 15); + b = F2(b, c, d, a, X[ 5], 9); + a = F2(a, b, c, d, X[ 2], 11); + d = F2(d, a, b, c, X[14], 7); + c = F2(c, d, a, b, X[11], 13); + b = F2(b, c, d, a, X[ 8], 12); + + // + // Round 3 + // + a = F3(a, b, c, d, X[ 3], 11); + d = F3(d, a, b, c, X[10], 13); + c = F3(c, d, a, b, X[14], 6); + b = F3(b, c, d, a, X[ 4], 7); + a = F3(a, b, c, d, X[ 9], 14); + d = F3(d, a, b, c, X[15], 9); + c = F3(c, d, a, b, X[ 8], 13); + b = F3(b, c, d, a, X[ 1], 15); + a = F3(a, b, c, d, X[ 2], 14); + d = F3(d, a, b, c, X[ 7], 8); + c = F3(c, d, a, b, X[ 0], 13); + b = F3(b, c, d, a, X[ 6], 6); + a = F3(a, b, c, d, X[13], 5); + d = F3(d, a, b, c, X[11], 12); + c = F3(c, d, a, b, X[ 5], 7); + b = F3(b, c, d, a, X[12], 5); + + // + // Round 4 + // + a = F4(a, b, c, d, X[ 1], 11); + d = F4(d, a, b, c, X[ 9], 12); + c = F4(c, d, a, b, X[11], 14); + b = F4(b, c, d, a, X[10], 15); + a = F4(a, b, c, d, X[ 0], 14); + d = F4(d, a, b, c, X[ 8], 15); + c = F4(c, d, a, b, X[12], 9); + b = F4(b, c, d, a, X[ 4], 8); + a = F4(a, b, c, d, X[13], 9); + d = F4(d, a, b, c, X[ 3], 14); + c = F4(c, d, a, b, X[ 7], 5); + b = F4(b, c, d, a, X[15], 6); + a = F4(a, b, c, d, X[14], 8); + d = F4(d, a, b, c, X[ 5], 6); + c = F4(c, d, a, b, X[ 6], 5); + b = F4(b, c, d, a, X[ 2], 12); + + // + // Parallel round 1 + // + aa = FF4(aa, bb, cc, dd, X[ 5], 8); + dd = FF4(dd, aa, bb, cc, X[14], 9); + cc = FF4(cc, dd, aa, bb, X[ 7], 9); + bb = FF4(bb, cc, dd, aa, X[ 0], 11); + aa = FF4(aa, bb, cc, dd, X[ 9], 13); + dd = FF4(dd, aa, bb, cc, X[ 2], 15); + cc = FF4(cc, dd, aa, bb, X[11], 15); + bb = FF4(bb, cc, dd, aa, X[ 4], 5); + aa = FF4(aa, bb, cc, dd, X[13], 7); + dd = FF4(dd, aa, bb, cc, X[ 6], 7); + cc = FF4(cc, dd, aa, bb, X[15], 8); + bb = FF4(bb, cc, dd, aa, X[ 8], 11); + aa = FF4(aa, bb, cc, dd, X[ 1], 14); + dd = FF4(dd, aa, bb, cc, X[10], 14); + cc = FF4(cc, dd, aa, bb, X[ 3], 12); + bb = FF4(bb, cc, dd, aa, X[12], 6); + + // + // Parallel round 2 + // + aa = FF3(aa, bb, cc, dd, X[ 6], 9); + dd = FF3(dd, aa, bb, cc, X[11], 13); + cc = FF3(cc, dd, aa, bb, X[ 3], 15); + bb = FF3(bb, cc, dd, aa, X[ 7], 7); + aa = FF3(aa, bb, cc, dd, X[ 0], 12); + dd = FF3(dd, aa, bb, cc, X[13], 8); + cc = FF3(cc, dd, aa, bb, X[ 5], 9); + bb = FF3(bb, cc, dd, aa, X[10], 11); + aa = FF3(aa, bb, cc, dd, X[14], 7); + dd = FF3(dd, aa, bb, cc, X[15], 7); + cc = FF3(cc, dd, aa, bb, X[ 8], 12); + bb = FF3(bb, cc, dd, aa, X[12], 7); + aa = FF3(aa, bb, cc, dd, X[ 4], 6); + dd = FF3(dd, aa, bb, cc, X[ 9], 15); + cc = FF3(cc, dd, aa, bb, X[ 1], 13); + bb = FF3(bb, cc, dd, aa, X[ 2], 11); + + // + // Parallel round 3 + // + aa = FF2(aa, bb, cc, dd, X[15], 9); + dd = FF2(dd, aa, bb, cc, X[ 5], 7); + cc = FF2(cc, dd, aa, bb, X[ 1], 15); + bb = FF2(bb, cc, dd, aa, X[ 3], 11); + aa = FF2(aa, bb, cc, dd, X[ 7], 8); + dd = FF2(dd, aa, bb, cc, X[14], 6); + cc = FF2(cc, dd, aa, bb, X[ 6], 6); + bb = FF2(bb, cc, dd, aa, X[ 9], 14); + aa = FF2(aa, bb, cc, dd, X[11], 12); + dd = FF2(dd, aa, bb, cc, X[ 8], 13); + cc = FF2(cc, dd, aa, bb, X[12], 5); + bb = FF2(bb, cc, dd, aa, X[ 2], 14); + aa = FF2(aa, bb, cc, dd, X[10], 13); + dd = FF2(dd, aa, bb, cc, X[ 0], 13); + cc = FF2(cc, dd, aa, bb, X[ 4], 7); + bb = FF2(bb, cc, dd, aa, X[13], 5); + + // + // Parallel round 4 + // + aa = FF1(aa, bb, cc, dd, X[ 8], 15); + dd = FF1(dd, aa, bb, cc, X[ 6], 5); + cc = FF1(cc, dd, aa, bb, X[ 4], 8); + bb = FF1(bb, cc, dd, aa, X[ 1], 11); + aa = FF1(aa, bb, cc, dd, X[ 3], 14); + dd = FF1(dd, aa, bb, cc, X[11], 14); + cc = FF1(cc, dd, aa, bb, X[15], 6); + bb = FF1(bb, cc, dd, aa, X[ 0], 14); + aa = FF1(aa, bb, cc, dd, X[ 5], 6); + dd = FF1(dd, aa, bb, cc, X[12], 9); + cc = FF1(cc, dd, aa, bb, X[ 2], 12); + bb = FF1(bb, cc, dd, aa, X[13], 9); + aa = FF1(aa, bb, cc, dd, X[ 9], 12); + dd = FF1(dd, aa, bb, cc, X[ 7], 5); + cc = FF1(cc, dd, aa, bb, X[10], 15); + bb = FF1(bb, cc, dd, aa, X[14], 8); + + dd += c + H1; // final result for H0 + + // + // combine the results + // + H1 = H2 + d + aa; + H2 = H3 + a + bb; + H3 = H0 + b + cc; + H0 = dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + } + +} diff --git a/src/core/srcbc/crypto/digests/RipeMD160Digest.cs b/src/core/srcbc/crypto/digests/RipeMD160Digest.cs new file mode 100644 index 0000000..3b4979c --- /dev/null +++ b/src/core/srcbc/crypto/digests/RipeMD160Digest.cs @@ -0,0 +1,423 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of RipeMD see, + * http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html + */ + public class RipeMD160Digest + : GeneralDigest + { + private const int DigestLength = 20; + + private int H0, H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public RipeMD160Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public RipeMD160Digest(RipeMD160Digest t) : base(t) + { + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "RIPEMD160"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong) bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint) word >> 8); + outBytes[outOff + 2] = (byte)((uint) word >> 16); + outBytes[outOff + 3] = (byte)((uint) word >> 24); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H0, output, outOff); + UnpackWord(H1, output, outOff + 4); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 12); + UnpackWord(H4, output, outOff + 16); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables to the IV values. + */ + public override void Reset() + { + base.Reset(); + + H0 = unchecked((int) 0x67452301); + H1 = unchecked((int) 0xefcdab89); + H2 = unchecked((int) 0x98badcfe); + H3 = unchecked((int) 0x10325476); + H4 = unchecked((int) 0xc3d2e1f0); + + xOff = 0; + + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (int) ((uint) x >> (32 - n)); + } + + /* + * f1,f2,f3,f4,f5 are the basic RipeMD160 functions. + */ + + /* + * rounds 0-15 + */ + private int F1( + int x, + int y, + int z) + { + return x ^ y ^ z; + } + + /* + * rounds 16-31 + */ + private int F2( + int x, + int y, + int z) + { + return (x & y) | (~x & z); + } + + /* + * rounds 32-47 + */ + private int F3( + int x, + int y, + int z) + { + return (x | ~y) ^ z; + } + + /* + * rounds 48-63 + */ + private int F4( + int x, + int y, + int z) + { + return (x & z) | (y & ~z); + } + + /* + * rounds 64-79 + */ + private int F5( + int x, + int y, + int z) + { + return x ^ (y | ~z); + } + + internal override void ProcessBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int e, ee; + + a = aa = H0; + b = bb = H1; + c = cc = H2; + d = dd = H3; + e = ee = H4; + + // + // Rounds 1 - 16 + // + // left + a = RL(a + F1(b,c,d) + X[ 0], 11) + e; c = RL(c, 10); + e = RL(e + F1(a,b,c) + X[ 1], 14) + d; b = RL(b, 10); + d = RL(d + F1(e,a,b) + X[ 2], 15) + c; a = RL(a, 10); + c = RL(c + F1(d,e,a) + X[ 3], 12) + b; e = RL(e, 10); + b = RL(b + F1(c,d,e) + X[ 4], 5) + a; d = RL(d, 10); + a = RL(a + F1(b,c,d) + X[ 5], 8) + e; c = RL(c, 10); + e = RL(e + F1(a,b,c) + X[ 6], 7) + d; b = RL(b, 10); + d = RL(d + F1(e,a,b) + X[ 7], 9) + c; a = RL(a, 10); + c = RL(c + F1(d,e,a) + X[ 8], 11) + b; e = RL(e, 10); + b = RL(b + F1(c,d,e) + X[ 9], 13) + a; d = RL(d, 10); + a = RL(a + F1(b,c,d) + X[10], 14) + e; c = RL(c, 10); + e = RL(e + F1(a,b,c) + X[11], 15) + d; b = RL(b, 10); + d = RL(d + F1(e,a,b) + X[12], 6) + c; a = RL(a, 10); + c = RL(c + F1(d,e,a) + X[13], 7) + b; e = RL(e, 10); + b = RL(b + F1(c,d,e) + X[14], 9) + a; d = RL(d, 10); + a = RL(a + F1(b,c,d) + X[15], 8) + e; c = RL(c, 10); + + // right + aa = RL(aa + F5(bb,cc,dd) + X[ 5] + unchecked((int) 0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa,bb,cc) + X[14] + unchecked((int) 0x50a28be6), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee,aa,bb) + X[ 7] + unchecked((int) 0x50a28be6), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd,ee,aa) + X[ 0] + unchecked((int) 0x50a28be6), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc,dd,ee) + X[ 9] + unchecked((int) 0x50a28be6), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb,cc,dd) + X[ 2] + unchecked((int) 0x50a28be6), 15) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa,bb,cc) + X[11] + unchecked((int) 0x50a28be6), 15) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee,aa,bb) + X[ 4] + unchecked((int) 0x50a28be6), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd,ee,aa) + X[13] + unchecked((int) 0x50a28be6), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc,dd,ee) + X[ 6] + unchecked((int) 0x50a28be6), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb,cc,dd) + X[15] + unchecked((int) 0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa,bb,cc) + X[ 8] + unchecked((int) 0x50a28be6), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee,aa,bb) + X[ 1] + unchecked((int) 0x50a28be6), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd,ee,aa) + X[10] + unchecked((int) 0x50a28be6), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc,dd,ee) + X[ 3] + unchecked((int) 0x50a28be6), 12) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb,cc,dd) + X[12] + unchecked((int) 0x50a28be6), 6) + ee; cc = RL(cc, 10); + + // + // Rounds 16-31 + // + // left + e = RL(e + F2(a,b,c) + X[ 7] + unchecked((int) 0x5a827999), 7) + d; b = RL(b, 10); + d = RL(d + F2(e,a,b) + X[ 4] + unchecked((int) 0x5a827999), 6) + c; a = RL(a, 10); + c = RL(c + F2(d,e,a) + X[13] + unchecked((int) 0x5a827999), 8) + b; e = RL(e, 10); + b = RL(b + F2(c,d,e) + X[ 1] + unchecked((int) 0x5a827999), 13) + a; d = RL(d, 10); + a = RL(a + F2(b,c,d) + X[10] + unchecked((int) 0x5a827999), 11) + e; c = RL(c, 10); + e = RL(e + F2(a,b,c) + X[ 6] + unchecked((int) 0x5a827999), 9) + d; b = RL(b, 10); + d = RL(d + F2(e,a,b) + X[15] + unchecked((int) 0x5a827999), 7) + c; a = RL(a, 10); + c = RL(c + F2(d,e,a) + X[ 3] + unchecked((int) 0x5a827999), 15) + b; e = RL(e, 10); + b = RL(b + F2(c,d,e) + X[12] + unchecked((int) 0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b,c,d) + X[ 0] + unchecked((int) 0x5a827999), 12) + e; c = RL(c, 10); + e = RL(e + F2(a,b,c) + X[ 9] + unchecked((int) 0x5a827999), 15) + d; b = RL(b, 10); + d = RL(d + F2(e,a,b) + X[ 5] + unchecked((int) 0x5a827999), 9) + c; a = RL(a, 10); + c = RL(c + F2(d,e,a) + X[ 2] + unchecked((int) 0x5a827999), 11) + b; e = RL(e, 10); + b = RL(b + F2(c,d,e) + X[14] + unchecked((int) 0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b,c,d) + X[11] + unchecked((int) 0x5a827999), 13) + e; c = RL(c, 10); + e = RL(e + F2(a,b,c) + X[ 8] + unchecked((int) 0x5a827999), 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + F4(aa,bb,cc) + X[ 6] + unchecked((int) 0x5c4dd124), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee,aa,bb) + X[11] + unchecked((int) 0x5c4dd124), 13) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd,ee,aa) + X[ 3] + unchecked((int) 0x5c4dd124), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc,dd,ee) + X[ 7] + unchecked((int) 0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb,cc,dd) + X[ 0] + unchecked((int) 0x5c4dd124), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa,bb,cc) + X[13] + unchecked((int) 0x5c4dd124), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee,aa,bb) + X[ 5] + unchecked((int) 0x5c4dd124), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd,ee,aa) + X[10] + unchecked((int) 0x5c4dd124), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc,dd,ee) + X[14] + unchecked((int) 0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb,cc,dd) + X[15] + unchecked((int) 0x5c4dd124), 7) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa,bb,cc) + X[ 8] + unchecked((int) 0x5c4dd124), 12) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee,aa,bb) + X[12] + unchecked((int) 0x5c4dd124), 7) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd,ee,aa) + X[ 4] + unchecked((int) 0x5c4dd124), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc,dd,ee) + X[ 9] + unchecked((int) 0x5c4dd124), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb,cc,dd) + X[ 1] + unchecked((int) 0x5c4dd124), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa,bb,cc) + X[ 2] + unchecked((int) 0x5c4dd124), 11) + dd; bb = RL(bb, 10); + + // + // Rounds 32-47 + // + // left + d = RL(d + F3(e,a,b) + X[ 3] + unchecked((int) 0x6ed9eba1), 11) + c; a = RL(a, 10); + c = RL(c + F3(d,e,a) + X[10] + unchecked((int) 0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c,d,e) + X[14] + unchecked((int) 0x6ed9eba1), 6) + a; d = RL(d, 10); + a = RL(a + F3(b,c,d) + X[ 4] + unchecked((int) 0x6ed9eba1), 7) + e; c = RL(c, 10); + e = RL(e + F3(a,b,c) + X[ 9] + unchecked((int) 0x6ed9eba1), 14) + d; b = RL(b, 10); + d = RL(d + F3(e,a,b) + X[15] + unchecked((int) 0x6ed9eba1), 9) + c; a = RL(a, 10); + c = RL(c + F3(d,e,a) + X[ 8] + unchecked((int) 0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c,d,e) + X[ 1] + unchecked((int) 0x6ed9eba1), 15) + a; d = RL(d, 10); + a = RL(a + F3(b,c,d) + X[ 2] + unchecked((int) 0x6ed9eba1), 14) + e; c = RL(c, 10); + e = RL(e + F3(a,b,c) + X[ 7] + unchecked((int) 0x6ed9eba1), 8) + d; b = RL(b, 10); + d = RL(d + F3(e,a,b) + X[ 0] + unchecked((int) 0x6ed9eba1), 13) + c; a = RL(a, 10); + c = RL(c + F3(d,e,a) + X[ 6] + unchecked((int) 0x6ed9eba1), 6) + b; e = RL(e, 10); + b = RL(b + F3(c,d,e) + X[13] + unchecked((int) 0x6ed9eba1), 5) + a; d = RL(d, 10); + a = RL(a + F3(b,c,d) + X[11] + unchecked((int) 0x6ed9eba1), 12) + e; c = RL(c, 10); + e = RL(e + F3(a,b,c) + X[ 5] + unchecked((int) 0x6ed9eba1), 7) + d; b = RL(b, 10); + d = RL(d + F3(e,a,b) + X[12] + unchecked((int) 0x6ed9eba1), 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + F3(ee,aa,bb) + X[15] + unchecked((int) 0x6d703ef3), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd,ee,aa) + X[ 5] + unchecked((int) 0x6d703ef3), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc,dd,ee) + X[ 1] + unchecked((int) 0x6d703ef3), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb,cc,dd) + X[ 3] + unchecked((int) 0x6d703ef3), 11) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa,bb,cc) + X[ 7] + unchecked((int) 0x6d703ef3), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee,aa,bb) + X[14] + unchecked((int) 0x6d703ef3), 6) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd,ee,aa) + X[ 6] + unchecked((int) 0x6d703ef3), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc,dd,ee) + X[ 9] + unchecked((int) 0x6d703ef3), 14) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb,cc,dd) + X[11] + unchecked((int) 0x6d703ef3), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa,bb,cc) + X[ 8] + unchecked((int) 0x6d703ef3), 13) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee,aa,bb) + X[12] + unchecked((int) 0x6d703ef3), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd,ee,aa) + X[ 2] + unchecked((int) 0x6d703ef3), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc,dd,ee) + X[10] + unchecked((int) 0x6d703ef3), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb,cc,dd) + X[ 0] + unchecked((int) 0x6d703ef3), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa,bb,cc) + X[ 4] + unchecked((int) 0x6d703ef3), 7) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee,aa,bb) + X[13] + unchecked((int) 0x6d703ef3), 5) + cc; aa = RL(aa, 10); + + // + // Rounds 48-63 + // + // left + c = RL(c + F4(d,e,a) + X[ 1] + unchecked((int) 0x8f1bbcdc), 11) + b; e = RL(e, 10); + b = RL(b + F4(c,d,e) + X[ 9] + unchecked((int) 0x8f1bbcdc), 12) + a; d = RL(d, 10); + a = RL(a + F4(b,c,d) + X[11] + unchecked((int) 0x8f1bbcdc), 14) + e; c = RL(c, 10); + e = RL(e + F4(a,b,c) + X[10] + unchecked((int) 0x8f1bbcdc), 15) + d; b = RL(b, 10); + d = RL(d + F4(e,a,b) + X[ 0] + unchecked((int) 0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d,e,a) + X[ 8] + unchecked((int) 0x8f1bbcdc), 15) + b; e = RL(e, 10); + b = RL(b + F4(c,d,e) + X[12] + unchecked((int) 0x8f1bbcdc), 9) + a; d = RL(d, 10); + a = RL(a + F4(b,c,d) + X[ 4] + unchecked((int) 0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a,b,c) + X[13] + unchecked((int) 0x8f1bbcdc), 9) + d; b = RL(b, 10); + d = RL(d + F4(e,a,b) + X[ 3] + unchecked((int) 0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d,e,a) + X[ 7] + unchecked((int) 0x8f1bbcdc), 5) + b; e = RL(e, 10); + b = RL(b + F4(c,d,e) + X[15] + unchecked((int) 0x8f1bbcdc), 6) + a; d = RL(d, 10); + a = RL(a + F4(b,c,d) + X[14] + unchecked((int) 0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a,b,c) + X[ 5] + unchecked((int) 0x8f1bbcdc), 6) + d; b = RL(b, 10); + d = RL(d + F4(e,a,b) + X[ 6] + unchecked((int) 0x8f1bbcdc), 5) + c; a = RL(a, 10); + c = RL(c + F4(d,e,a) + X[ 2] + unchecked((int) 0x8f1bbcdc), 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + F2(dd,ee,aa) + X[ 8] + unchecked((int) 0x7a6d76e9), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc,dd,ee) + X[ 6] + unchecked((int) 0x7a6d76e9), 5) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb,cc,dd) + X[ 4] + unchecked((int) 0x7a6d76e9), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa,bb,cc) + X[ 1] + unchecked((int) 0x7a6d76e9), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee,aa,bb) + X[ 3] + unchecked((int) 0x7a6d76e9), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd,ee,aa) + X[11] + unchecked((int) 0x7a6d76e9), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc,dd,ee) + X[15] + unchecked((int) 0x7a6d76e9), 6) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb,cc,dd) + X[ 0] + unchecked((int) 0x7a6d76e9), 14) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa,bb,cc) + X[ 5] + unchecked((int) 0x7a6d76e9), 6) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee,aa,bb) + X[12] + unchecked((int) 0x7a6d76e9), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd,ee,aa) + X[ 2] + unchecked((int) 0x7a6d76e9), 12) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc,dd,ee) + X[13] + unchecked((int) 0x7a6d76e9), 9) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb,cc,dd) + X[ 9] + unchecked((int) 0x7a6d76e9), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa,bb,cc) + X[ 7] + unchecked((int) 0x7a6d76e9), 5) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee,aa,bb) + X[10] + unchecked((int) 0x7a6d76e9), 15) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd,ee,aa) + X[14] + unchecked((int) 0x7a6d76e9), 8) + bb; ee = RL(ee, 10); + + // + // Rounds 64-79 + // + // left + b = RL(b + F5(c,d,e) + X[ 4] + unchecked((int) 0xa953fd4e), 9) + a; d = RL(d, 10); + a = RL(a + F5(b,c,d) + X[ 0] + unchecked((int) 0xa953fd4e), 15) + e; c = RL(c, 10); + e = RL(e + F5(a,b,c) + X[ 5] + unchecked((int) 0xa953fd4e), 5) + d; b = RL(b, 10); + d = RL(d + F5(e,a,b) + X[ 9] + unchecked((int) 0xa953fd4e), 11) + c; a = RL(a, 10); + c = RL(c + F5(d,e,a) + X[ 7] + unchecked((int) 0xa953fd4e), 6) + b; e = RL(e, 10); + b = RL(b + F5(c,d,e) + X[12] + unchecked((int) 0xa953fd4e), 8) + a; d = RL(d, 10); + a = RL(a + F5(b,c,d) + X[ 2] + unchecked((int) 0xa953fd4e), 13) + e; c = RL(c, 10); + e = RL(e + F5(a,b,c) + X[10] + unchecked((int) 0xa953fd4e), 12) + d; b = RL(b, 10); + d = RL(d + F5(e,a,b) + X[14] + unchecked((int) 0xa953fd4e), 5) + c; a = RL(a, 10); + c = RL(c + F5(d,e,a) + X[ 1] + unchecked((int) 0xa953fd4e), 12) + b; e = RL(e, 10); + b = RL(b + F5(c,d,e) + X[ 3] + unchecked((int) 0xa953fd4e), 13) + a; d = RL(d, 10); + a = RL(a + F5(b,c,d) + X[ 8] + unchecked((int) 0xa953fd4e), 14) + e; c = RL(c, 10); + e = RL(e + F5(a,b,c) + X[11] + unchecked((int) 0xa953fd4e), 11) + d; b = RL(b, 10); + d = RL(d + F5(e,a,b) + X[ 6] + unchecked((int) 0xa953fd4e), 8) + c; a = RL(a, 10); + c = RL(c + F5(d,e,a) + X[15] + unchecked((int) 0xa953fd4e), 5) + b; e = RL(e, 10); + b = RL(b + F5(c,d,e) + X[13] + unchecked((int) 0xa953fd4e), 6) + a; d = RL(d, 10); + + // right + bb = RL(bb + F1(cc,dd,ee) + X[12], 8) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb,cc,dd) + X[15], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa,bb,cc) + X[10], 12) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee,aa,bb) + X[ 4], 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd,ee,aa) + X[ 1], 12) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc,dd,ee) + X[ 5], 5) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb,cc,dd) + X[ 8], 14) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa,bb,cc) + X[ 7], 6) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee,aa,bb) + X[ 6], 8) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd,ee,aa) + X[ 2], 13) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc,dd,ee) + X[13], 6) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb,cc,dd) + X[14], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa,bb,cc) + X[ 0], 15) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee,aa,bb) + X[ 3], 13) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd,ee,aa) + X[ 9], 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc,dd,ee) + X[11], 11) + aa; dd = RL(dd, 10); + + dd += c + H1; + H1 = H2 + d + ee; + H2 = H3 + e + aa; + H3 = H4 + a + bb; + H4 = H0 + b + cc; + H0 = dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + } + +} diff --git a/src/core/srcbc/crypto/digests/RipeMD256Digest.cs b/src/core/srcbc/crypto/digests/RipeMD256Digest.cs new file mode 100644 index 0000000..ccaa774 --- /dev/null +++ b/src/core/srcbc/crypto/digests/RipeMD256Digest.cs @@ -0,0 +1,409 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /// + ///

Implementation of RipeMD256.

+ ///

Note: this algorithm offers the same level of security as RipeMD128.

+ ///
+ public class RipeMD256Digest + : GeneralDigest + { + public override string AlgorithmName + { + get { return "RIPEMD256"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + private const int DigestLength = 32; + + private int H0, H1, H2, H3, H4, H5, H6, H7; // IV's + + private int[] X = new int[16]; + private int xOff; + + /// Standard constructor + public RipeMD256Digest() + { + Reset(); + } + + /// Copy constructor. This will copy the state of the provided + /// message digest. + /// + public RipeMD256Digest(RipeMD256Digest t):base(t) + { + + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong)bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)(uint)word; + outBytes[outOff + 1] = (byte)((uint)word >> 8); + outBytes[outOff + 2] = (byte)((uint)word >> 16); + outBytes[outOff + 3] = (byte)((uint)word >> 24); + } + + public override int DoFinal(byte[] output, int outOff) + { + Finish(); + + UnpackWord(H0, output, outOff); + UnpackWord(H1, output, outOff + 4); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 12); + UnpackWord(H4, output, outOff + 16); + UnpackWord(H5, output, outOff + 20); + UnpackWord(H6, output, outOff + 24); + UnpackWord(H7, output, outOff + 28); + + Reset(); + + return DigestLength; + } + + /// reset the chaining variables to the IV values. + public override void Reset() + { + base.Reset(); + + H0 = unchecked((int)0x67452301); + H1 = unchecked((int)0xefcdab89); + H2 = unchecked((int)0x98badcfe); + H3 = unchecked((int)0x10325476); + H4 = unchecked((int)0x76543210); + H5 = unchecked((int)0xFEDCBA98); + H6 = unchecked((int)0x89ABCDEF); + H7 = unchecked((int)0x01234567); + + xOff = 0; + + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (int)((uint)x >> (32 - n)); + } + + /* + * f1,f2,f3,f4 are the basic RipeMD128 functions. + */ + + /* + * F + */ + private int F1(int x, int y, int z) + { + return x ^ y ^ z; + } + + /* + * G + */ + private int F2(int x, int y, int z) + { + return (x & y) | (~ x & z); + } + + /* + * H + */ + private int F3(int x, int y, int z) + { + return (x | ~ y) ^ z; + } + + /* + * I + */ + private int F4(int x, int y, int z) + { + return (x & z) | (y & ~ z); + } + + private int F1(int a, int b, int c, int d, int x, int s) + { + return RL(a + F1(b, c, d) + x, s); + } + + private int F2(int a, int b, int c, int d, int x, int s) + { + return RL(a + F2(b, c, d) + x + unchecked((int)0x5a827999), s); + } + + private int F3(int a, int b, int c, int d, int x, int s) + { + return RL(a + F3(b, c, d) + x + unchecked((int)0x6ed9eba1), s); + } + + private int F4(int a, int b, int c, int d, int x, int s) + { + return RL(a + F4(b, c, d) + x + unchecked((int)0x8f1bbcdc), s); + } + + private int FF1(int a, int b, int c, int d, int x, int s) + { + return RL(a + F1(b, c, d) + x, s); + } + + private int FF2(int a, int b, int c, int d, int x, int s) + { + return RL(a + F2(b, c, d) + x + unchecked((int)0x6d703ef3), s); + } + + private int FF3(int a, int b, int c, int d, int x, int s) + { + return RL(a + F3(b, c, d) + x + unchecked((int)0x5c4dd124), s); + } + + private int FF4(int a, int b, int c, int d, int x, int s) + { + return RL(a + F4(b, c, d) + x + unchecked((int)0x50a28be6), s); + } + + internal override void ProcessBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int t; + + a = H0; + b = H1; + c = H2; + d = H3; + aa = H4; + bb = H5; + cc = H6; + dd = H7; + + // + // Round 1 + // + + a = F1(a, b, c, d, X[0], 11); + d = F1(d, a, b, c, X[1], 14); + c = F1(c, d, a, b, X[2], 15); + b = F1(b, c, d, a, X[3], 12); + a = F1(a, b, c, d, X[4], 5); + d = F1(d, a, b, c, X[5], 8); + c = F1(c, d, a, b, X[6], 7); + b = F1(b, c, d, a, X[7], 9); + a = F1(a, b, c, d, X[8], 11); + d = F1(d, a, b, c, X[9], 13); + c = F1(c, d, a, b, X[10], 14); + b = F1(b, c, d, a, X[11], 15); + a = F1(a, b, c, d, X[12], 6); + d = F1(d, a, b, c, X[13], 7); + c = F1(c, d, a, b, X[14], 9); + b = F1(b, c, d, a, X[15], 8); + + aa = FF4(aa, bb, cc, dd, X[5], 8); + dd = FF4(dd, aa, bb, cc, X[14], 9); + cc = FF4(cc, dd, aa, bb, X[7], 9); + bb = FF4(bb, cc, dd, aa, X[0], 11); + aa = FF4(aa, bb, cc, dd, X[9], 13); + dd = FF4(dd, aa, bb, cc, X[2], 15); + cc = FF4(cc, dd, aa, bb, X[11], 15); + bb = FF4(bb, cc, dd, aa, X[4], 5); + aa = FF4(aa, bb, cc, dd, X[13], 7); + dd = FF4(dd, aa, bb, cc, X[6], 7); + cc = FF4(cc, dd, aa, bb, X[15], 8); + bb = FF4(bb, cc, dd, aa, X[8], 11); + aa = FF4(aa, bb, cc, dd, X[1], 14); + dd = FF4(dd, aa, bb, cc, X[10], 14); + cc = FF4(cc, dd, aa, bb, X[3], 12); + bb = FF4(bb, cc, dd, aa, X[12], 6); + + t = a; a = aa; aa = t; + + // + // Round 2 + // + a = F2(a, b, c, d, X[7], 7); + d = F2(d, a, b, c, X[4], 6); + c = F2(c, d, a, b, X[13], 8); + b = F2(b, c, d, a, X[1], 13); + a = F2(a, b, c, d, X[10], 11); + d = F2(d, a, b, c, X[6], 9); + c = F2(c, d, a, b, X[15], 7); + b = F2(b, c, d, a, X[3], 15); + a = F2(a, b, c, d, X[12], 7); + d = F2(d, a, b, c, X[0], 12); + c = F2(c, d, a, b, X[9], 15); + b = F2(b, c, d, a, X[5], 9); + a = F2(a, b, c, d, X[2], 11); + d = F2(d, a, b, c, X[14], 7); + c = F2(c, d, a, b, X[11], 13); + b = F2(b, c, d, a, X[8], 12); + + aa = FF3(aa, bb, cc, dd, X[6], 9); + dd = FF3(dd, aa, bb, cc, X[11], 13); + cc = FF3(cc, dd, aa, bb, X[3], 15); + bb = FF3(bb, cc, dd, aa, X[7], 7); + aa = FF3(aa, bb, cc, dd, X[0], 12); + dd = FF3(dd, aa, bb, cc, X[13], 8); + cc = FF3(cc, dd, aa, bb, X[5], 9); + bb = FF3(bb, cc, dd, aa, X[10], 11); + aa = FF3(aa, bb, cc, dd, X[14], 7); + dd = FF3(dd, aa, bb, cc, X[15], 7); + cc = FF3(cc, dd, aa, bb, X[8], 12); + bb = FF3(bb, cc, dd, aa, X[12], 7); + aa = FF3(aa, bb, cc, dd, X[4], 6); + dd = FF3(dd, aa, bb, cc, X[9], 15); + cc = FF3(cc, dd, aa, bb, X[1], 13); + bb = FF3(bb, cc, dd, aa, X[2], 11); + + t = b; b = bb; bb = t; + + // + // Round 3 + // + a = F3(a, b, c, d, X[3], 11); + d = F3(d, a, b, c, X[10], 13); + c = F3(c, d, a, b, X[14], 6); + b = F3(b, c, d, a, X[4], 7); + a = F3(a, b, c, d, X[9], 14); + d = F3(d, a, b, c, X[15], 9); + c = F3(c, d, a, b, X[8], 13); + b = F3(b, c, d, a, X[1], 15); + a = F3(a, b, c, d, X[2], 14); + d = F3(d, a, b, c, X[7], 8); + c = F3(c, d, a, b, X[0], 13); + b = F3(b, c, d, a, X[6], 6); + a = F3(a, b, c, d, X[13], 5); + d = F3(d, a, b, c, X[11], 12); + c = F3(c, d, a, b, X[5], 7); + b = F3(b, c, d, a, X[12], 5); + + aa = FF2(aa, bb, cc, dd, X[15], 9); + dd = FF2(dd, aa, bb, cc, X[5], 7); + cc = FF2(cc, dd, aa, bb, X[1], 15); + bb = FF2(bb, cc, dd, aa, X[3], 11); + aa = FF2(aa, bb, cc, dd, X[7], 8); + dd = FF2(dd, aa, bb, cc, X[14], 6); + cc = FF2(cc, dd, aa, bb, X[6], 6); + bb = FF2(bb, cc, dd, aa, X[9], 14); + aa = FF2(aa, bb, cc, dd, X[11], 12); + dd = FF2(dd, aa, bb, cc, X[8], 13); + cc = FF2(cc, dd, aa, bb, X[12], 5); + bb = FF2(bb, cc, dd, aa, X[2], 14); + aa = FF2(aa, bb, cc, dd, X[10], 13); + dd = FF2(dd, aa, bb, cc, X[0], 13); + cc = FF2(cc, dd, aa, bb, X[4], 7); + bb = FF2(bb, cc, dd, aa, X[13], 5); + + t = c; c = cc; cc = t; + + // + // Round 4 + // + a = F4(a, b, c, d, X[1], 11); + d = F4(d, a, b, c, X[9], 12); + c = F4(c, d, a, b, X[11], 14); + b = F4(b, c, d, a, X[10], 15); + a = F4(a, b, c, d, X[0], 14); + d = F4(d, a, b, c, X[8], 15); + c = F4(c, d, a, b, X[12], 9); + b = F4(b, c, d, a, X[4], 8); + a = F4(a, b, c, d, X[13], 9); + d = F4(d, a, b, c, X[3], 14); + c = F4(c, d, a, b, X[7], 5); + b = F4(b, c, d, a, X[15], 6); + a = F4(a, b, c, d, X[14], 8); + d = F4(d, a, b, c, X[5], 6); + c = F4(c, d, a, b, X[6], 5); + b = F4(b, c, d, a, X[2], 12); + + aa = FF1(aa, bb, cc, dd, X[8], 15); + dd = FF1(dd, aa, bb, cc, X[6], 5); + cc = FF1(cc, dd, aa, bb, X[4], 8); + bb = FF1(bb, cc, dd, aa, X[1], 11); + aa = FF1(aa, bb, cc, dd, X[3], 14); + dd = FF1(dd, aa, bb, cc, X[11], 14); + cc = FF1(cc, dd, aa, bb, X[15], 6); + bb = FF1(bb, cc, dd, aa, X[0], 14); + aa = FF1(aa, bb, cc, dd, X[5], 6); + dd = FF1(dd, aa, bb, cc, X[12], 9); + cc = FF1(cc, dd, aa, bb, X[2], 12); + bb = FF1(bb, cc, dd, aa, X[13], 9); + aa = FF1(aa, bb, cc, dd, X[9], 12); + dd = FF1(dd, aa, bb, cc, X[7], 5); + cc = FF1(cc, dd, aa, bb, X[10], 15); + bb = FF1(bb, cc, dd, aa, X[14], 8); + + t = d; d = dd; dd = t; + + H0 += a; + H1 += b; + H2 += c; + H3 += d; + H4 += aa; + H5 += bb; + H6 += cc; + H7 += dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + } +} diff --git a/src/core/srcbc/crypto/digests/RipeMD320Digest.cs b/src/core/srcbc/crypto/digests/RipeMD320Digest.cs new file mode 100644 index 0000000..dad6801 --- /dev/null +++ b/src/core/srcbc/crypto/digests/RipeMD320Digest.cs @@ -0,0 +1,438 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /// + ///

Implementation of RipeMD 320.

+ ///

Note: this algorithm offers the same level of security as RipeMD160.

+ ///
+ public class RipeMD320Digest + : GeneralDigest + { + public override string AlgorithmName + { + get { return "RIPEMD320"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + private const int DigestLength = 40; + + private int H0, H1, H2, H3, H4, H5, H6, H7, H8, H9; // IV's + + private int[] X = new int[16]; + private int xOff; + + /// Standard constructor + public RipeMD320Digest() + { + Reset(); + } + + /// Copy constructor. This will copy the state of the provided + /// message digest. + /// + public RipeMD320Digest(RipeMD320Digest t) + : base(t) + { + + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + H9 = t.H9; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong)bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint)word >> 8); + outBytes[outOff + 2] = (byte)((uint)word >> 16); + outBytes[outOff + 3] = (byte)((uint)word >> 24); + } + + public override int DoFinal(byte[] output, int outOff) + { + Finish(); + + UnpackWord(H0, output, outOff); + UnpackWord(H1, output, outOff + 4); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 12); + UnpackWord(H4, output, outOff + 16); + UnpackWord(H5, output, outOff + 20); + UnpackWord(H6, output, outOff + 24); + UnpackWord(H7, output, outOff + 28); + UnpackWord(H8, output, outOff + 32); + UnpackWord(H9, output, outOff + 36); + + Reset(); + + return DigestLength; + } + + /// reset the chaining variables to the IV values. + public override void Reset() + { + base.Reset(); + + H0 = unchecked((int) 0x67452301); + H1 = unchecked((int) 0xefcdab89); + H2 = unchecked((int) 0x98badcfe); + H3 = unchecked((int) 0x10325476); + H4 = unchecked((int) 0xc3d2e1f0); + H5 = unchecked((int) 0x76543210); + H6 = unchecked((int) 0xFEDCBA98); + H7 = unchecked((int) 0x89ABCDEF); + H8 = unchecked((int) 0x01234567); + H9 = unchecked((int) 0x3C2D1E0F); + + xOff = 0; + + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (int)(((uint)x) >> (32 - n)); + } + + /* + * f1,f2,f3,f4,f5 are the basic RipeMD160 functions. + */ + + /* + * rounds 0-15 + */ + private int F1(int x, int y, int z) + { + return x ^ y ^ z; + } + + /* + * rounds 16-31 + */ + private int F2(int x, int y, int z) + { + return (x & y) | (~ x & z); + } + + /* + * rounds 32-47 + */ + private int F3(int x, int y, int z) + { + return (x | ~ y) ^ z; + } + + /* + * rounds 48-63 + */ + private int F4(int x, int y, int z) + { + return (x & z) | (y & ~ z); + } + + /* + * rounds 64-79 + */ + private int F5(int x, int y, int z) + { + return x ^ (y | ~z); + } + + internal override void ProcessBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int e, ee; + int t; + + a = H0; + b = H1; + c = H2; + d = H3; + e = H4; + aa = H5; + bb = H6; + cc = H7; + dd = H8; + ee = H9; + + // + // Rounds 1 - 16 + // + // left + a = RL(a + F1(b, c, d) + X[0], 11) + e; c = RL(c, 10); + e = RL(e + F1(a, b, c) + X[1], 14) + d; b = RL(b, 10); + d = RL(d + F1(e, a, b) + X[2], 15) + c; a = RL(a, 10); + c = RL(c + F1(d, e, a) + X[3], 12) + b; e = RL(e, 10); + b = RL(b + F1(c, d, e) + X[4], 5) + a; d = RL(d, 10); + a = RL(a + F1(b, c, d) + X[5], 8) + e; c = RL(c, 10); + e = RL(e + F1(a, b, c) + X[6], 7) + d; b = RL(b, 10); + d = RL(d + F1(e, a, b) + X[7], 9) + c; a = RL(a, 10); + c = RL(c + F1(d, e, a) + X[8], 11) + b; e = RL(e, 10); + b = RL(b + F1(c, d, e) + X[9], 13) + a; d = RL(d, 10); + a = RL(a + F1(b, c, d) + X[10], 14) + e; c = RL(c, 10); + e = RL(e + F1(a, b, c) + X[11], 15) + d; b = RL(b, 10); + d = RL(d + F1(e, a, b) + X[12], 6) + c; a = RL(a, 10); + c = RL(c + F1(d, e, a) + X[13], 7) + b; e = RL(e, 10); + b = RL(b + F1(c, d, e) + X[14], 9) + a; d = RL(d, 10); + a = RL(a + F1(b, c, d) + X[15], 8) + e; c = RL(c, 10); + + // right + aa = RL(aa + F5(bb, cc, dd) + X[5] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + X[14] + unchecked((int)0x50a28be6), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + X[7] + unchecked((int)0x50a28be6), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + X[0] + unchecked((int)0x50a28be6), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + X[9] + unchecked((int)0x50a28be6), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + X[2] + unchecked((int)0x50a28be6), 15) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + X[11] + unchecked((int)0x50a28be6), 15) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + X[4] + unchecked((int)0x50a28be6), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + X[13] + unchecked((int)0x50a28be6), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + X[6] + unchecked((int)0x50a28be6), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + X[15] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + X[8] + unchecked((int)0x50a28be6), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + X[1] + unchecked((int)0x50a28be6), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + X[10] + unchecked((int)0x50a28be6), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + X[3] + unchecked((int)0x50a28be6), 12) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + X[12] + unchecked((int)0x50a28be6), 6) + ee; cc = RL(cc, 10); + + t = a; a = aa; aa = t; + // + // Rounds 16-31 + // + // left + e = RL(e + F2(a, b, c) + X[7] + unchecked((int)0x5a827999), 7) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + X[4] + unchecked((int)0x5a827999), 6) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + X[13] + unchecked((int)0x5a827999), 8) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + X[1] + unchecked((int)0x5a827999), 13) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + X[10] + unchecked((int)0x5a827999), 11) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + X[6] + unchecked((int)0x5a827999), 9) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + X[15] + unchecked((int)0x5a827999), 7) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + X[3] + unchecked((int)0x5a827999), 15) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + X[12] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + X[0] + unchecked((int)0x5a827999), 12) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + X[9] + unchecked((int)0x5a827999), 15) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + X[5] + unchecked((int)0x5a827999), 9) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + X[2] + unchecked((int)0x5a827999), 11) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + X[14] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + X[11] + unchecked((int)0x5a827999), 13) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + X[8] + unchecked((int)0x5a827999), 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + F4(aa, bb, cc) + X[6] + unchecked((int)0x5c4dd124), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + X[11] + unchecked((int)0x5c4dd124), 13) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + X[3] + unchecked((int)0x5c4dd124), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + X[7] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + X[0] + unchecked((int)0x5c4dd124), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + X[13] + unchecked((int)0x5c4dd124), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + X[5] + unchecked((int)0x5c4dd124), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + X[10] + unchecked((int)0x5c4dd124), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + X[14] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + X[15] + unchecked((int)0x5c4dd124), 7) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + X[8] + unchecked((int)0x5c4dd124), 12) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + X[12] + unchecked((int)0x5c4dd124), 7) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + X[4] + unchecked((int)0x5c4dd124), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + X[9] + unchecked((int)0x5c4dd124), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + X[1] + unchecked((int)0x5c4dd124), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + X[2] + unchecked((int)0x5c4dd124), 11) + dd; bb = RL(bb, 10); + + t = b; b = bb; bb = t; + + // + // Rounds 32-47 + // + // left + d = RL(d + F3(e, a, b) + X[3] + unchecked((int)0x6ed9eba1), 11) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + X[10] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + X[14] + unchecked((int)0x6ed9eba1), 6) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + X[4] + unchecked((int)0x6ed9eba1), 7) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + X[9] + unchecked((int)0x6ed9eba1), 14) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + X[15] + unchecked((int)0x6ed9eba1), 9) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + X[8] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + X[1] + unchecked((int)0x6ed9eba1), 15) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + X[2] + unchecked((int)0x6ed9eba1), 14) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + X[7] + unchecked((int)0x6ed9eba1), 8) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + X[0] + unchecked((int)0x6ed9eba1), 13) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + X[6] + unchecked((int)0x6ed9eba1), 6) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + X[13] + unchecked((int)0x6ed9eba1), 5) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + X[11] + unchecked((int)0x6ed9eba1), 12) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + X[5] + unchecked((int)0x6ed9eba1), 7) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + X[12] + unchecked((int)0x6ed9eba1), 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + F3(ee, aa, bb) + X[15] + unchecked((int)0x6d703ef3), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + X[5] + unchecked((int)0x6d703ef3), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + X[1] + unchecked((int)0x6d703ef3), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + X[3] + unchecked((int)0x6d703ef3), 11) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + X[7] + unchecked((int)0x6d703ef3), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + X[14] + unchecked((int)0x6d703ef3), 6) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + X[6] + unchecked((int)0x6d703ef3), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + X[9] + unchecked((int)0x6d703ef3), 14) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + X[11] + unchecked((int)0x6d703ef3), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + X[8] + unchecked((int)0x6d703ef3), 13) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + X[12] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + X[2] + unchecked((int)0x6d703ef3), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + X[10] + unchecked((int)0x6d703ef3), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + X[0] + unchecked((int)0x6d703ef3), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + X[4] + unchecked((int)0x6d703ef3), 7) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + X[13] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); + + t = c; c = cc; cc = t; + + // + // Rounds 48-63 + // + // left + c = RL(c + F4(d, e, a) + X[1] + unchecked((int)0x8f1bbcdc), 11) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + X[9] + unchecked((int)0x8f1bbcdc), 12) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + X[11] + unchecked((int)0x8f1bbcdc), 14) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + X[10] + unchecked((int)0x8f1bbcdc), 15) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + X[0] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + X[8] + unchecked((int)0x8f1bbcdc), 15) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + X[12] + unchecked((int)0x8f1bbcdc), 9) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + X[4] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + X[13] + unchecked((int)0x8f1bbcdc), 9) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + X[3] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + X[7] + unchecked((int)0x8f1bbcdc), 5) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + X[15] + unchecked((int)0x8f1bbcdc), 6) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + X[14] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + X[5] + unchecked((int)0x8f1bbcdc), 6) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + X[6] + unchecked((int)0x8f1bbcdc), 5) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + X[2] + unchecked((int)0x8f1bbcdc), 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + F2(dd, ee, aa) + X[8] + unchecked((int)0x7a6d76e9), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + X[6] + unchecked((int)0x7a6d76e9), 5) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + X[4] + unchecked((int)0x7a6d76e9), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + X[1] + unchecked((int)0x7a6d76e9), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + X[3] + unchecked((int)0x7a6d76e9), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + X[11] + unchecked((int)0x7a6d76e9), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + X[15] + unchecked((int)0x7a6d76e9), 6) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + X[0] + unchecked((int)0x7a6d76e9), 14) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + X[5] + unchecked((int)0x7a6d76e9), 6) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + X[12] + unchecked((int)0x7a6d76e9), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + X[2] + unchecked((int)0x7a6d76e9), 12) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + X[13] + unchecked((int)0x7a6d76e9), 9) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + X[9] + unchecked((int)0x7a6d76e9), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + X[7] + unchecked((int)0x7a6d76e9), 5) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + X[10] + unchecked((int)0x7a6d76e9), 15) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + X[14] + unchecked((int)0x7a6d76e9), 8) + bb; ee = RL(ee, 10); + + t = d; d = dd; dd = t; + + // + // Rounds 64-79 + // + // left + b = RL(b + F5(c, d, e) + X[4] + unchecked((int)0xa953fd4e), 9) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + X[0] + unchecked((int)0xa953fd4e), 15) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + X[5] + unchecked((int)0xa953fd4e), 5) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + X[9] + unchecked((int)0xa953fd4e), 11) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + X[7] + unchecked((int)0xa953fd4e), 6) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + X[12] + unchecked((int)0xa953fd4e), 8) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + X[2] + unchecked((int)0xa953fd4e), 13) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + X[10] + unchecked((int)0xa953fd4e), 12) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + X[14] + unchecked((int)0xa953fd4e), 5) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + X[1] + unchecked((int)0xa953fd4e), 12) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + X[3] + unchecked((int)0xa953fd4e), 13) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + X[8] + unchecked((int)0xa953fd4e), 14) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + X[11] + unchecked((int)0xa953fd4e), 11) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + X[6] + unchecked((int)0xa953fd4e), 8) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + X[15] + unchecked((int)0xa953fd4e), 5) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + X[13] + unchecked((int)0xa953fd4e), 6) + a; d = RL(d, 10); + + // right + bb = RL(bb + F1(cc, dd, ee) + X[12], 8) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb, cc, dd) + X[15], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa, bb, cc) + X[10], 12) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee, aa, bb) + X[4], 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd, ee, aa) + X[1], 12) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc, dd, ee) + X[5], 5) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb, cc, dd) + X[8], 14) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa, bb, cc) + X[7], 6) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee, aa, bb) + X[6], 8) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd, ee, aa) + X[2], 13) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc, dd, ee) + X[13], 6) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb, cc, dd) + X[14], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa, bb, cc) + X[0], 15) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee, aa, bb) + X[3], 13) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd, ee, aa) + X[9], 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc, dd, ee) + X[11], 11) + aa; dd = RL(dd, 10); + + // + // do (e, ee) swap as part of assignment. + // + + H0 += a; + H1 += b; + H2 += c; + H3 += d; + H4 += ee; + H5 += aa; + H6 += bb; + H7 += cc; + H8 += dd; + H9 += e; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + } +} diff --git a/src/core/srcbc/crypto/digests/Sha1Digest.cs b/src/core/srcbc/crypto/digests/Sha1Digest.cs new file mode 100644 index 0000000..de469b4 --- /dev/null +++ b/src/core/srcbc/crypto/digests/Sha1Digest.cs @@ -0,0 +1,285 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /** + * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349. + * + * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5 + * is the "endienness" of the word processing! + */ + public class Sha1Digest + : GeneralDigest + { + private const int DigestLength = 20; + + private int H1, H2, H3, H4, H5; + + private int[] X = new int[80]; + private int xOff; + + public Sha1Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha1Digest(Sha1Digest t) + : base(t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "SHA-1"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = ((input[inOff] & 0xff) << 24) | ((input[inOff + 1] & 0xff) << 16) + | ((input[inOff + 2] & 0xff) << 8) | ((input[inOff + 3] & 0xff)); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + private static void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff++] = (byte)((uint)word >> 24); + outBytes[outOff++] = (byte)((uint)word >> 16); + outBytes[outOff++] = (byte)((uint)word >> 8); + outBytes[outOff++] = (byte)word; + } + + internal override void ProcessLength(long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)((ulong) bitLength >> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 4); + UnpackWord(H3, output, outOff + 8); + UnpackWord(H4, output, outOff + 12); + UnpackWord(H5, output, outOff + 16); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + H1 = unchecked( (int) 0x67452301 ); + H2 = unchecked( (int) 0xefcdab89 ); + H3 = unchecked( (int) 0x98badcfe ); + H4 = unchecked( (int) 0x10325476 ); + H5 = unchecked( (int) 0xc3d2e1f0 ); + + xOff = 0; + for (int i = 0; i != X.Length; i++) X[i] = 0; + } + + // + // Additive constants + // + private const int Y1 = unchecked( (int) 0x5a827999); + private const int Y2 = unchecked( (int) 0x6ed9eba1); + private const int Y3 = unchecked( (int) 0x8f1bbcdc); + private const int Y4 = unchecked( (int) 0xca62c1d6); + + private static int F( + int u, + int v, + int w) + { + return ((u & v) | ((~u) & w)); + } + + private static int H( + int u, + int v, + int w) + { + return (u ^ v ^ w); + } + + private static int G( + int u, + int v, + int w) + { + return ((u & v) | (u & w) | (v & w)); + } + + internal override void ProcessBlock() + { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i < 80; i++) + { + int t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]; + X[i] = t << 1 | (int)((uint)t >> 31); + } + + // + // set up working variables. + // + int A = H1; + int B = H2; + int C = H3; + int D = H4; + int E = H5; + + // + // round 1 + // + int idx = 0; + + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 + // B = rotateLeft(B, 30) + E += (A << 5 | (int)((uint)A >> 27)) + F(B, C, D) + X[idx++] + Y1; + B = B << 30 | (int)((uint)B >> 2); + + D += (E << 5 | (int)((uint)E >> 27)) + F(A, B, C) + X[idx++] + Y1; + A = A << 30 | (int)((uint)A >> 2); + + C += (D << 5 | (int)((uint)D >> 27)) + F(E, A, B) + X[idx++] + Y1; + E = E << 30 | (int)((uint)E >> 2); + + B += (C << 5 | (int)((uint)C >> 27)) + F(D, E, A) + X[idx++] + Y1; + D = D << 30 | (int)((uint)D >> 2); + + A += (B << 5 | (int)((uint)B >> 27)) + F(C, D, E) + X[idx++] + Y1; + C = C << 30 | (int)((uint)C >> 2); + } + + // + // round 2 + // + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 + // B = rotateLeft(B, 30) + E += (A << 5 | (int)((uint)A >> 27)) + H(B, C, D) + X[idx++] + Y2; + B = B << 30 | (int)((uint)B >> 2); + + D += (E << 5 | (int)((uint)E >> 27)) + H(A, B, C) + X[idx++] + Y2; + A = A << 30 | (int)((uint)A >> 2); + + C += (D << 5 | (int)((uint)D >> 27)) + H(E, A, B) + X[idx++] + Y2; + E = E << 30 | (int)((uint)E >> 2); + + B += (C << 5 | (int)((uint)C >> 27)) + H(D, E, A) + X[idx++] + Y2; + D = D << 30 | (int)((uint)D >> 2); + + A += (B << 5 | (int)((uint)B >> 27)) + H(C, D, E) + X[idx++] + Y2; + C = C << 30 | (int)((uint)C >> 2); + } + + // + // round 3 + // + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 + // B = rotateLeft(B, 30) + E += (A << 5 | (int)((uint)A >> 27)) + G(B, C, D) + X[idx++] + Y3; + B = B << 30 | (int)((uint)B >> 2); + + D += (E << 5 | (int)((uint)E >> 27)) + G(A, B, C) + X[idx++] + Y3; + A = A << 30 | (int)((uint)A >> 2); + + C += (D << 5 | (int)((uint)D >> 27)) + G(E, A, B) + X[idx++] + Y3; + E = E << 30 | (int)((uint)E >> 2); + + B += (C << 5 | (int)((uint)C >> 27)) + G(D, E, A) + X[idx++] + Y3; + D = D << 30 | (int)((uint)D >> 2); + + A += (B << 5 | (int)((uint)B >> 27)) + G(C, D, E) + X[idx++] + Y3; + C = C << 30 | (int)((uint)C >> 2); + } + + // + // round 4 + // + for (int j = 0; j <= 3; j++) + { + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 + // B = rotateLeft(B, 30) + E += (A << 5 | (int)((uint)A >> 27)) + H(B, C, D) + X[idx++] + Y4; + B = B << 30 | (int)((uint)B >> 2); + + D += (E << 5 | (int)((uint)E >> 27)) + H(A, B, C) + X[idx++] + Y4; + A = A << 30 | (int)((uint)A >> 2); + + C += (D << 5 | (int)((uint)D >> 27)) + H(E, A, B) + X[idx++] + Y4; + E = E << 30 | (int)((uint)E >> 2); + + B += (C << 5 | (int)((uint)C >> 27)) + H(D, E, A) + X[idx++] + Y4; + D = D << 30 | (int)((uint)D >> 2); + + A += (B << 5 | (int)((uint)B >> 27)) + H(C, D, E) + X[idx++] + Y4; + C = C << 30 | (int)((uint)C >> 2); + } + + H1 += A; + H2 += B; + H3 += C; + H4 += D; + H5 += E; + + // + // reset start of the buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + } +} diff --git a/src/core/srcbc/crypto/digests/Sha224Digest.cs b/src/core/srcbc/crypto/digests/Sha224Digest.cs new file mode 100644 index 0000000..da51ccf --- /dev/null +++ b/src/core/srcbc/crypto/digests/Sha224Digest.cs @@ -0,0 +1,287 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * SHA-224 as described in RFC 3874 + *
+     *         block  word  digest
+     * SHA-1   512    32    160
+     * SHA-224 512    32    224
+     * SHA-256 512    32    256
+     * SHA-384 1024   64    384
+     * SHA-512 1024   64    512
+     * 
+ */ + public class Sha224Digest + : GeneralDigest + { + private const int DigestLength = 28; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + /** + * Standard constructor + */ + public Sha224Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha224Digest( + Sha224Digest t) + : base(t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "SHA-224"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = ((input[inOff] & 0xff) << 24) | ((input[inOff + 1] & 0xff) << 16) + | ((input[inOff + 2] & 0xff) << 8) | ((input[inOff + 3] & 0xff)); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)((uint) word >> 24); + outBytes[outOff + 1] = (byte)((uint) word >> 16); + outBytes[outOff + 2] = (byte)((uint) word >> 8); + outBytes[outOff + 3] = (byte)word; + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)((ulong) bitLength >> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 4); + UnpackWord(H3, output, outOff + 8); + UnpackWord(H4, output, outOff + 12); + UnpackWord(H5, output, outOff + 16); + UnpackWord(H6, output, outOff + 20); + UnpackWord(H7, output, outOff + 24); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + /* SHA-224 initial hash value + */ + + unchecked + { + H1 = (int) 0xc1059ed8; + H2 = (int) 0x367cd507; + H3 = (int) 0x3070dd17; + H4 = (int) 0xf70e5939; + H5 = (int) 0xffc00b31; + H6 = (int) 0x68581511; + H7 = (int) 0x64f98fa7; + H8 = (int) 0xbefa4fa4; + } + + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + internal override void ProcessBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int ti = 16; ti <= 63; ti++) + { + X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16]; + } + + // + // set up working variables. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + int t = 0; + for(int i = 0; i < 8; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + (int)K[t] + X[t++]; + d += h; + h += Sum0(a) + Maj(a, b, c); + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + (int)K[t] + X[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + (int)K[t] + X[t++]; + b += f; + f += Sum0(g) + Maj(g, h, a); + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + (int)K[t] + X[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + (int)K[t] + X[t++]; + h += d; + d += Sum0(e) + Maj(e, f, g); + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + (int)K[t] + X[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + (int)K[t] + X[t++]; + f += b; + b += Sum0(c) + Maj(c, d, e); + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + (int)K[t] + X[t++]; + e += a; + a += Sum0(b) + Maj(b, c, d); + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + + Array.Clear(X, 0, 16); + } + + /* SHA-224 functions */ + private static int Ch( + int x, + int y, + int z) + { + return ((x & y) ^ ((~x) & z)); + } + + private static int Maj( + int x, + int y, + int z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private static int Sum0( + int x) + { + return (((int)((uint)x >> 2)) | (x << 30)) ^ (((int)((uint)x >> 13)) | (x << 19)) ^ (((int)((uint)x >> 22)) | (x << 10)); + } + + private static int Sum1( + int x) + { + return (((int)((uint)x >> 6)) | (x << 26)) ^ (((int)((uint)x >> 11)) | (x << 21)) ^ (((int)((uint)x >> 25)) | (x << 7)); + } + + private static int Theta0( + int x) + { + return (((int)((uint)x >> 7)) | (x << 25)) ^ (((int)((uint)x >> 18)) | (x << 14)) ^ ((int)((uint)x >> 3)); + } + + private static int Theta1( + int x) + { + return (((int)((uint)x >> 17)) | (x << 15)) ^ (((int)((uint)x >> 19)) | (x << 13)) ^ ((int)((uint)x >> 10)); + } + + /* SHA-224 Constants + * (represent the first 32 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + internal static readonly uint[] K = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + } +} diff --git a/src/core/srcbc/crypto/digests/Sha256Digest.cs b/src/core/srcbc/crypto/digests/Sha256Digest.cs new file mode 100644 index 0000000..de9c9da --- /dev/null +++ b/src/core/srcbc/crypto/digests/Sha256Digest.cs @@ -0,0 +1,310 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-256. Note: As this is + * based on a draft this implementation is subject to change. + * + *
+    *         block  word  digest
+    * SHA-1   512    32    160
+    * SHA-256 512    32    256
+    * SHA-384 1024   64    384
+    * SHA-512 1024   64    512
+    * 
+ */ + public class Sha256Digest + : GeneralDigest + { + private const int DigestLength = 32; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + public Sha256Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha256Digest(Sha256Digest t) : base(t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "SHA-256"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = ((input[inOff] & 0xff) << 24) | ((input[inOff + 1] & 0xff) << 16) + | ((input[inOff + 2] & 0xff) << 8) | ((input[inOff + 3] & 0xff)); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)((uint) word >> 24); + outBytes[outOff + 1] = (byte)((uint) word >> 16); + outBytes[outOff + 2] = (byte)((uint) word >> 8); + outBytes[outOff + 3] = (byte)word; + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)((ulong) bitLength >> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 4); + UnpackWord(H3, output, outOff + 8); + UnpackWord(H4, output, outOff + 12); + UnpackWord(H5, output, outOff + 16); + UnpackWord(H6, output, outOff + 20); + UnpackWord(H7, output, outOff + 24); + UnpackWord(H8, output, outOff + 28); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + /* SHA-256 initial hash value + * The first 32 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + unchecked + { + H1 = (int) 0x6a09e667; + H2 = (int) 0xbb67ae85; + H3 = (int) 0x3c6ef372; + H4 = (int) 0xa54ff53a; + H5 = (int) 0x510e527f; + H6 = (int) 0x9b05688c; + H7 = (int) 0x1f83d9ab; + H8 = (int) 0x5be0cd19; + } + + xOff = 0; + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + internal override void ProcessBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int ti = 16; ti <= 63; ti++) + { + X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16]; + } + + // + // set up working variables. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + int t = 0; + for(int i = 0; i < 8; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t++]; + d += h; + h += Sum0(a) + Maj(a, b, c); + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t++]; + b += f; + f += Sum0(g) + Maj(g, h, a); + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t++]; + h += d; + d += Sum0(e) + Maj(e, f, g); + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t++]; + f += b; + b += Sum0(c) + Maj(c, d, e); + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t++]; + e += a; + a += Sum0(b) + Maj(b, c, d); + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + + Array.Clear(X, 0, 16); + } + + /* SHA-256 functions */ + private static int Ch( + int x, + int y, + int z) + { + return ((x & y) ^ ((~x) & z)); + } + + private static int Maj( + int x, + int y, + int z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private static int Sum0( + int x) + { + return (((int)((uint)x >> 2)) | (x << 30)) ^ (((int)((uint)x >> 13)) | (x << 19)) ^ (((int)((uint)x >> 22)) | (x << 10)); + } + + private static int Sum1( + int x) + { + return (((int)((uint)x >> 6)) | (x << 26)) ^ (((int)((uint)x >> 11)) | (x << 21)) ^ (((int)((uint)x >> 25)) | (x << 7)); + } + + private static int Theta0( + int x) + { + return (((int)((uint)x >> 7)) | (x << 25)) ^ (((int)((uint)x >> 18)) | (x << 14)) ^ ((int)((uint)x >> 3)); + } + + private static int Theta1( + int x) + { + return (((int)((uint)x >> 17)) | (x << 15)) ^ (((int)((uint)x >> 19)) | (x << 13)) ^ ((int)((uint)x >> 10)); + } + + /* SHA-256 Constants + * (represent the first 32 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + internal static readonly int[] K = { + unchecked ((int) 0x428a2f98), unchecked ((int) 0x71374491), + unchecked ((int) 0xb5c0fbcf), unchecked ((int) 0xe9b5dba5), + unchecked ((int) 0x3956c25b), unchecked ((int) 0x59f111f1), + unchecked ((int) 0x923f82a4), unchecked ((int) 0xab1c5ed5), + unchecked ((int) 0xd807aa98), unchecked ((int) 0x12835b01), + unchecked ((int) 0x243185be), unchecked ((int) 0x550c7dc3), + unchecked ((int) 0x72be5d74), unchecked ((int) 0x80deb1fe), + unchecked ((int) 0x9bdc06a7), unchecked ((int) 0xc19bf174), + unchecked ((int) 0xe49b69c1), unchecked ((int) 0xefbe4786), + unchecked ((int) 0x0fc19dc6), unchecked ((int) 0x240ca1cc), + unchecked ((int) 0x2de92c6f), unchecked ((int) 0x4a7484aa), + unchecked ((int) 0x5cb0a9dc), unchecked ((int) 0x76f988da), + unchecked ((int) 0x983e5152), unchecked ((int) 0xa831c66d), + unchecked ((int) 0xb00327c8), unchecked ((int) 0xbf597fc7), + unchecked ((int) 0xc6e00bf3), unchecked ((int) 0xd5a79147), + unchecked ((int) 0x06ca6351), unchecked ((int) 0x14292967), + unchecked ((int) 0x27b70a85), unchecked ((int) 0x2e1b2138), + unchecked ((int) 0x4d2c6dfc), unchecked ((int) 0x53380d13), + unchecked ((int) 0x650a7354), unchecked ((int) 0x766a0abb), + unchecked ((int) 0x81c2c92e), unchecked ((int) 0x92722c85), + unchecked ((int) 0xa2bfe8a1), unchecked ((int) 0xa81a664b), + unchecked ((int) 0xc24b8b70), unchecked ((int) 0xc76c51a3), + unchecked ((int) 0xd192e819), unchecked ((int) 0xd6990624), + unchecked ((int) 0xf40e3585), unchecked ((int) 0x106aa070), + unchecked ((int) 0x19a4c116), unchecked ((int) 0x1e376c08), + unchecked ((int) 0x2748774c), unchecked ((int) 0x34b0bcb5), + unchecked ((int) 0x391c0cb3), unchecked ((int) 0x4ed8aa4a), + unchecked ((int) 0x5b9cca4f), unchecked ((int) 0x682e6ff3), + unchecked ((int) 0x748f82ee), unchecked ((int) 0x78a5636f), + unchecked ((int) 0x84c87814), unchecked ((int) 0x8cc70208), + unchecked ((int) 0x90befffa), unchecked ((int) 0xa4506ceb), + unchecked ((int) 0xbef9a3f7), unchecked ((int) 0xc67178f2) + }; + } +} diff --git a/src/core/srcbc/crypto/digests/Sha384Digest.cs b/src/core/srcbc/crypto/digests/Sha384Digest.cs new file mode 100644 index 0000000..7fe6daf --- /dev/null +++ b/src/core/srcbc/crypto/digests/Sha384Digest.cs @@ -0,0 +1,85 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-384. Note: As this is + * based on a draft this implementation is subject to change. + * + *
+     *         block  word  digest
+     * SHA-1   512    32    160
+     * SHA-256 512    32    256
+     * SHA-384 1024   64    384
+     * SHA-512 1024   64    512
+     * 
+ */ + public class Sha384Digest + : LongDigest + { + private const int DigestLength = 48; + + public Sha384Digest() + { + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha384Digest( + Sha384Digest t) + : base(t) + { + } + + public override string AlgorithmName + { + get { return "SHA-384"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 16); + UnpackWord(H4, output, outOff + 24); + UnpackWord(H5, output, outOff + 32); + UnpackWord(H6, output, outOff + 40); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + /* SHA-384 initial hash value + * The first 64 bits of the fractional parts of the square roots + * of the 9th through 16th prime numbers + */ + H1 = unchecked((long) 0xcbbb9d5dc1059ed8L); + H2 = unchecked((long) 0x629a292a367cd507L); + H3 = unchecked((long) 0x9159015a3070dd17L); + H4 = unchecked((long) 0x152fecd8f70e5939L); + H5 = unchecked((long) 0x67332667ffc00b31L); + H6 = unchecked((long) 0x8eb44a8768581511L); + H7 = unchecked((long) 0xdb0c2e0d64f98fa7L); + H8 = unchecked((long) 0x47b5481dbefa4fa4L); + } + } +} diff --git a/src/core/srcbc/crypto/digests/Sha512Digest.cs b/src/core/srcbc/crypto/digests/Sha512Digest.cs new file mode 100644 index 0000000..4c47f4b --- /dev/null +++ b/src/core/srcbc/crypto/digests/Sha512Digest.cs @@ -0,0 +1,88 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-512. Note: As this is + * based on a draft this implementation is subject to change. + * + *
+     *         block  word  digest
+     * SHA-1   512    32    160
+     * SHA-256 512    32    256
+     * SHA-384 1024   64    384
+     * SHA-512 1024   64    512
+     * 
+ */ + public class Sha512Digest + : LongDigest + { + private const int DigestLength = 64; + + public Sha512Digest() + { + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha512Digest( + Sha512Digest t) + : base(t) + { + } + + public override string AlgorithmName + { + get { return "SHA-512"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 16); + UnpackWord(H4, output, outOff + 24); + UnpackWord(H5, output, outOff + 32); + UnpackWord(H6, output, outOff + 40); + UnpackWord(H7, output, outOff + 48); + UnpackWord(H8, output, outOff + 56); + + Reset(); + + return DigestLength; + + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + /* SHA-512 initial hash value + * The first 64 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + H1 = unchecked((long) 0x6a09e667f3bcc908L); + H2 = unchecked((long) 0xbb67ae8584caa73bL); + H3 = unchecked((long) 0x3c6ef372fe94f82bL); + H4 = unchecked((long) 0xa54ff53a5f1d36f1L); + H5 = unchecked((long) 0x510e527fade682d1L); + H6 = unchecked((long) 0x9b05688c2b3e6c1fL); + H7 = unchecked((long) 0x1f83d9abfb41bd6bL); + H8 = unchecked((long) 0x5be0cd19137e2179L); + } + } +} diff --git a/src/core/srcbc/crypto/digests/ShortenedDigest.cs b/src/core/srcbc/crypto/digests/ShortenedDigest.cs new file mode 100644 index 0000000..de0d325 --- /dev/null +++ b/src/core/srcbc/crypto/digests/ShortenedDigest.cs @@ -0,0 +1,82 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Wrapper class that reduces the output length of a particular digest to + * only the first n bytes of the digest function. + */ + public class ShortenedDigest + : IDigest + { + private IDigest baseDigest; + private int length; + + /** + * Base constructor. + * + * @param baseDigest underlying digest to use. + * @param length length in bytes of the output of doFinal. + * @exception ArgumentException if baseDigest is null, or length is greater than baseDigest.GetDigestSize(). + */ + public ShortenedDigest( + IDigest baseDigest, + int length) + { + if (baseDigest == null) + { + throw new ArgumentNullException("baseDigest"); + } + + if (length > baseDigest.GetDigestSize()) + { + throw new ArgumentException("baseDigest output not large enough to support length"); + } + + this.baseDigest = baseDigest; + this.length = length; + } + + public string AlgorithmName + { + get { return baseDigest.AlgorithmName + "(" + length * 8 + ")"; } + } + + public int GetDigestSize() + { + return length; + } + + public void Update(byte input) + { + baseDigest.Update(input); + } + + public void BlockUpdate(byte[] input, int inOff, int length) + { + baseDigest.BlockUpdate(input, inOff, length); + } + + public int DoFinal(byte[] output, int outOff) + { + byte[] tmp = new byte[baseDigest.GetDigestSize()]; + + baseDigest.DoFinal(tmp, 0); + + Array.Copy(tmp, 0, output, outOff, length); + + return length; + } + + public void Reset() + { + baseDigest.Reset(); + } + + public int GetByteLength() + { + return baseDigest.GetByteLength(); + } + } +} diff --git a/src/core/srcbc/crypto/digests/TigerDigest.cs b/src/core/srcbc/crypto/digests/TigerDigest.cs new file mode 100644 index 0000000..16a2b1c --- /dev/null +++ b/src/core/srcbc/crypto/digests/TigerDigest.cs @@ -0,0 +1,868 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of Tiger based on: + * + * http://www.cs.technion.ac.il/~biham/Reports/Tiger + */ + public class TigerDigest + : IDigest + { + private const int MyByteLength = 64; + + /* + * S-Boxes. + */ + private static readonly long[] t1 = { + unchecked((long) 0x02AAB17CF7E90C5EL) /* 0 */, unchecked((long) 0xAC424B03E243A8ECL) /* 1 */, + unchecked((long) 0x72CD5BE30DD5FCD3L) /* 2 */, unchecked((long) 0x6D019B93F6F97F3AL) /* 3 */, + unchecked((long) 0xCD9978FFD21F9193L) /* 4 */, unchecked((long) 0x7573A1C9708029E2L) /* 5 */, + unchecked((long) 0xB164326B922A83C3L) /* 6 */, unchecked((long) 0x46883EEE04915870L) /* 7 */, + unchecked((long) 0xEAACE3057103ECE6L) /* 8 */, unchecked((long) 0xC54169B808A3535CL) /* 9 */, + unchecked((long) 0x4CE754918DDEC47CL) /* 10 */, unchecked((long) 0x0AA2F4DFDC0DF40CL) /* 11 */, + unchecked((long) 0x10B76F18A74DBEFAL) /* 12 */, unchecked((long) 0xC6CCB6235AD1AB6AL) /* 13 */, + unchecked((long) 0x13726121572FE2FFL) /* 14 */, unchecked((long) 0x1A488C6F199D921EL) /* 15 */, + unchecked((long) 0x4BC9F9F4DA0007CAL) /* 16 */, unchecked((long) 0x26F5E6F6E85241C7L) /* 17 */, + unchecked((long) 0x859079DBEA5947B6L) /* 18 */, unchecked((long) 0x4F1885C5C99E8C92L) /* 19 */, + unchecked((long) 0xD78E761EA96F864BL) /* 20 */, unchecked((long) 0x8E36428C52B5C17DL) /* 21 */, + unchecked((long) 0x69CF6827373063C1L) /* 22 */, unchecked((long) 0xB607C93D9BB4C56EL) /* 23 */, + unchecked((long) 0x7D820E760E76B5EAL) /* 24 */, unchecked((long) 0x645C9CC6F07FDC42L) /* 25 */, + unchecked((long) 0xBF38A078243342E0L) /* 26 */, unchecked((long) 0x5F6B343C9D2E7D04L) /* 27 */, + unchecked((long) 0xF2C28AEB600B0EC6L) /* 28 */, unchecked((long) 0x6C0ED85F7254BCACL) /* 29 */, + unchecked((long) 0x71592281A4DB4FE5L) /* 30 */, unchecked((long) 0x1967FA69CE0FED9FL) /* 31 */, + unchecked((long) 0xFD5293F8B96545DBL) /* 32 */, unchecked((long) 0xC879E9D7F2A7600BL) /* 33 */, + unchecked((long) 0x860248920193194EL) /* 34 */, unchecked((long) 0xA4F9533B2D9CC0B3L) /* 35 */, + unchecked((long) 0x9053836C15957613L) /* 36 */, unchecked((long) 0xDB6DCF8AFC357BF1L) /* 37 */, + unchecked((long) 0x18BEEA7A7A370F57L) /* 38 */, unchecked((long) 0x037117CA50B99066L) /* 39 */, + unchecked((long) 0x6AB30A9774424A35L) /* 40 */, unchecked((long) 0xF4E92F02E325249BL) /* 41 */, + unchecked((long) 0x7739DB07061CCAE1L) /* 42 */, unchecked((long) 0xD8F3B49CECA42A05L) /* 43 */, + unchecked((long) 0xBD56BE3F51382F73L) /* 44 */, unchecked((long) 0x45FAED5843B0BB28L) /* 45 */, + unchecked((long) 0x1C813D5C11BF1F83L) /* 46 */, unchecked((long) 0x8AF0E4B6D75FA169L) /* 47 */, + unchecked((long) 0x33EE18A487AD9999L) /* 48 */, unchecked((long) 0x3C26E8EAB1C94410L) /* 49 */, + unchecked((long) 0xB510102BC0A822F9L) /* 50 */, unchecked((long) 0x141EEF310CE6123BL) /* 51 */, + unchecked((long) 0xFC65B90059DDB154L) /* 52 */, unchecked((long) 0xE0158640C5E0E607L) /* 53 */, + unchecked((long) 0x884E079826C3A3CFL) /* 54 */, unchecked((long) 0x930D0D9523C535FDL) /* 55 */, + unchecked((long) 0x35638D754E9A2B00L) /* 56 */, unchecked((long) 0x4085FCCF40469DD5L) /* 57 */, + unchecked((long) 0xC4B17AD28BE23A4CL) /* 58 */, unchecked((long) 0xCAB2F0FC6A3E6A2EL) /* 59 */, + unchecked((long) 0x2860971A6B943FCDL) /* 60 */, unchecked((long) 0x3DDE6EE212E30446L) /* 61 */, + unchecked((long) 0x6222F32AE01765AEL) /* 62 */, unchecked((long) 0x5D550BB5478308FEL) /* 63 */, + unchecked((long) 0xA9EFA98DA0EDA22AL) /* 64 */, unchecked((long) 0xC351A71686C40DA7L) /* 65 */, + unchecked((long) 0x1105586D9C867C84L) /* 66 */, unchecked((long) 0xDCFFEE85FDA22853L) /* 67 */, + unchecked((long) 0xCCFBD0262C5EEF76L) /* 68 */, unchecked((long) 0xBAF294CB8990D201L) /* 69 */, + unchecked((long) 0xE69464F52AFAD975L) /* 70 */, unchecked((long) 0x94B013AFDF133E14L) /* 71 */, + unchecked((long) 0x06A7D1A32823C958L) /* 72 */, unchecked((long) 0x6F95FE5130F61119L) /* 73 */, + unchecked((long) 0xD92AB34E462C06C0L) /* 74 */, unchecked((long) 0xED7BDE33887C71D2L) /* 75 */, + unchecked((long) 0x79746D6E6518393EL) /* 76 */, unchecked((long) 0x5BA419385D713329L) /* 77 */, + unchecked((long) 0x7C1BA6B948A97564L) /* 78 */, unchecked((long) 0x31987C197BFDAC67L) /* 79 */, + unchecked((long) 0xDE6C23C44B053D02L) /* 80 */, unchecked((long) 0x581C49FED002D64DL) /* 81 */, + unchecked((long) 0xDD474D6338261571L) /* 82 */, unchecked((long) 0xAA4546C3E473D062L) /* 83 */, + unchecked((long) 0x928FCE349455F860L) /* 84 */, unchecked((long) 0x48161BBACAAB94D9L) /* 85 */, + unchecked((long) 0x63912430770E6F68L) /* 86 */, unchecked((long) 0x6EC8A5E602C6641CL) /* 87 */, + unchecked((long) 0x87282515337DDD2BL) /* 88 */, unchecked((long) 0x2CDA6B42034B701BL) /* 89 */, + unchecked((long) 0xB03D37C181CB096DL) /* 90 */, unchecked((long) 0xE108438266C71C6FL) /* 91 */, + unchecked((long) 0x2B3180C7EB51B255L) /* 92 */, unchecked((long) 0xDF92B82F96C08BBCL) /* 93 */, + unchecked((long) 0x5C68C8C0A632F3BAL) /* 94 */, unchecked((long) 0x5504CC861C3D0556L) /* 95 */, + unchecked((long) 0xABBFA4E55FB26B8FL) /* 96 */, unchecked((long) 0x41848B0AB3BACEB4L) /* 97 */, + unchecked((long) 0xB334A273AA445D32L) /* 98 */, unchecked((long) 0xBCA696F0A85AD881L) /* 99 */, + unchecked((long) 0x24F6EC65B528D56CL) /* 100 */, unchecked((long) 0x0CE1512E90F4524AL) /* 101 */, + unchecked((long) 0x4E9DD79D5506D35AL) /* 102 */, unchecked((long) 0x258905FAC6CE9779L) /* 103 */, + unchecked((long) 0x2019295B3E109B33L) /* 104 */, unchecked((long) 0xF8A9478B73A054CCL) /* 105 */, + unchecked((long) 0x2924F2F934417EB0L) /* 106 */, unchecked((long) 0x3993357D536D1BC4L) /* 107 */, + unchecked((long) 0x38A81AC21DB6FF8BL) /* 108 */, unchecked((long) 0x47C4FBF17D6016BFL) /* 109 */, + unchecked((long) 0x1E0FAADD7667E3F5L) /* 110 */, unchecked((long) 0x7ABCFF62938BEB96L) /* 111 */, + unchecked((long) 0xA78DAD948FC179C9L) /* 112 */, unchecked((long) 0x8F1F98B72911E50DL) /* 113 */, + unchecked((long) 0x61E48EAE27121A91L) /* 114 */, unchecked((long) 0x4D62F7AD31859808L) /* 115 */, + unchecked((long) 0xECEBA345EF5CEAEBL) /* 116 */, unchecked((long) 0xF5CEB25EBC9684CEL) /* 117 */, + unchecked((long) 0xF633E20CB7F76221L) /* 118 */, unchecked((long) 0xA32CDF06AB8293E4L) /* 119 */, + unchecked((long) 0x985A202CA5EE2CA4L) /* 120 */, unchecked((long) 0xCF0B8447CC8A8FB1L) /* 121 */, + unchecked((long) 0x9F765244979859A3L) /* 122 */, unchecked((long) 0xA8D516B1A1240017L) /* 123 */, + unchecked((long) 0x0BD7BA3EBB5DC726L) /* 124 */, unchecked((long) 0xE54BCA55B86ADB39L) /* 125 */, + unchecked((long) 0x1D7A3AFD6C478063L) /* 126 */, unchecked((long) 0x519EC608E7669EDDL) /* 127 */, + unchecked((long) 0x0E5715A2D149AA23L) /* 128 */, unchecked((long) 0x177D4571848FF194L) /* 129 */, + unchecked((long) 0xEEB55F3241014C22L) /* 130 */, unchecked((long) 0x0F5E5CA13A6E2EC2L) /* 131 */, + unchecked((long) 0x8029927B75F5C361L) /* 132 */, unchecked((long) 0xAD139FABC3D6E436L) /* 133 */, + unchecked((long) 0x0D5DF1A94CCF402FL) /* 134 */, unchecked((long) 0x3E8BD948BEA5DFC8L) /* 135 */, + unchecked((long) 0xA5A0D357BD3FF77EL) /* 136 */, unchecked((long) 0xA2D12E251F74F645L) /* 137 */, + unchecked((long) 0x66FD9E525E81A082L) /* 138 */, unchecked((long) 0x2E0C90CE7F687A49L) /* 139 */, + unchecked((long) 0xC2E8BCBEBA973BC5L) /* 140 */, unchecked((long) 0x000001BCE509745FL) /* 141 */, + unchecked((long) 0x423777BBE6DAB3D6L) /* 142 */, unchecked((long) 0xD1661C7EAEF06EB5L) /* 143 */, + unchecked((long) 0xA1781F354DAACFD8L) /* 144 */, unchecked((long) 0x2D11284A2B16AFFCL) /* 145 */, + unchecked((long) 0xF1FC4F67FA891D1FL) /* 146 */, unchecked((long) 0x73ECC25DCB920ADAL) /* 147 */, + unchecked((long) 0xAE610C22C2A12651L) /* 148 */, unchecked((long) 0x96E0A810D356B78AL) /* 149 */, + unchecked((long) 0x5A9A381F2FE7870FL) /* 150 */, unchecked((long) 0xD5AD62EDE94E5530L) /* 151 */, + unchecked((long) 0xD225E5E8368D1427L) /* 152 */, unchecked((long) 0x65977B70C7AF4631L) /* 153 */, + unchecked((long) 0x99F889B2DE39D74FL) /* 154 */, unchecked((long) 0x233F30BF54E1D143L) /* 155 */, + unchecked((long) 0x9A9675D3D9A63C97L) /* 156 */, unchecked((long) 0x5470554FF334F9A8L) /* 157 */, + unchecked((long) 0x166ACB744A4F5688L) /* 158 */, unchecked((long) 0x70C74CAAB2E4AEADL) /* 159 */, + unchecked((long) 0xF0D091646F294D12L) /* 160 */, unchecked((long) 0x57B82A89684031D1L) /* 161 */, + unchecked((long) 0xEFD95A5A61BE0B6BL) /* 162 */, unchecked((long) 0x2FBD12E969F2F29AL) /* 163 */, + unchecked((long) 0x9BD37013FEFF9FE8L) /* 164 */, unchecked((long) 0x3F9B0404D6085A06L) /* 165 */, + unchecked((long) 0x4940C1F3166CFE15L) /* 166 */, unchecked((long) 0x09542C4DCDF3DEFBL) /* 167 */, + unchecked((long) 0xB4C5218385CD5CE3L) /* 168 */, unchecked((long) 0xC935B7DC4462A641L) /* 169 */, + unchecked((long) 0x3417F8A68ED3B63FL) /* 170 */, unchecked((long) 0xB80959295B215B40L) /* 171 */, + unchecked((long) 0xF99CDAEF3B8C8572L) /* 172 */, unchecked((long) 0x018C0614F8FCB95DL) /* 173 */, + unchecked((long) 0x1B14ACCD1A3ACDF3L) /* 174 */, unchecked((long) 0x84D471F200BB732DL) /* 175 */, + unchecked((long) 0xC1A3110E95E8DA16L) /* 176 */, unchecked((long) 0x430A7220BF1A82B8L) /* 177 */, + unchecked((long) 0xB77E090D39DF210EL) /* 178 */, unchecked((long) 0x5EF4BD9F3CD05E9DL) /* 179 */, + unchecked((long) 0x9D4FF6DA7E57A444L) /* 180 */, unchecked((long) 0xDA1D60E183D4A5F8L) /* 181 */, + unchecked((long) 0xB287C38417998E47L) /* 182 */, unchecked((long) 0xFE3EDC121BB31886L) /* 183 */, + unchecked((long) 0xC7FE3CCC980CCBEFL) /* 184 */, unchecked((long) 0xE46FB590189BFD03L) /* 185 */, + unchecked((long) 0x3732FD469A4C57DCL) /* 186 */, unchecked((long) 0x7EF700A07CF1AD65L) /* 187 */, + unchecked((long) 0x59C64468A31D8859L) /* 188 */, unchecked((long) 0x762FB0B4D45B61F6L) /* 189 */, + unchecked((long) 0x155BAED099047718L) /* 190 */, unchecked((long) 0x68755E4C3D50BAA6L) /* 191 */, + unchecked((long) 0xE9214E7F22D8B4DFL) /* 192 */, unchecked((long) 0x2ADDBF532EAC95F4L) /* 193 */, + unchecked((long) 0x32AE3909B4BD0109L) /* 194 */, unchecked((long) 0x834DF537B08E3450L) /* 195 */, + unchecked((long) 0xFA209DA84220728DL) /* 196 */, unchecked((long) 0x9E691D9B9EFE23F7L) /* 197 */, + unchecked((long) 0x0446D288C4AE8D7FL) /* 198 */, unchecked((long) 0x7B4CC524E169785BL) /* 199 */, + unchecked((long) 0x21D87F0135CA1385L) /* 200 */, unchecked((long) 0xCEBB400F137B8AA5L) /* 201 */, + unchecked((long) 0x272E2B66580796BEL) /* 202 */, unchecked((long) 0x3612264125C2B0DEL) /* 203 */, + unchecked((long) 0x057702BDAD1EFBB2L) /* 204 */, unchecked((long) 0xD4BABB8EACF84BE9L) /* 205 */, + unchecked((long) 0x91583139641BC67BL) /* 206 */, unchecked((long) 0x8BDC2DE08036E024L) /* 207 */, + unchecked((long) 0x603C8156F49F68EDL) /* 208 */, unchecked((long) 0xF7D236F7DBEF5111L) /* 209 */, + unchecked((long) 0x9727C4598AD21E80L) /* 210 */, unchecked((long) 0xA08A0896670A5FD7L) /* 211 */, + unchecked((long) 0xCB4A8F4309EBA9CBL) /* 212 */, unchecked((long) 0x81AF564B0F7036A1L) /* 213 */, + unchecked((long) 0xC0B99AA778199ABDL) /* 214 */, unchecked((long) 0x959F1EC83FC8E952L) /* 215 */, + unchecked((long) 0x8C505077794A81B9L) /* 216 */, unchecked((long) 0x3ACAAF8F056338F0L) /* 217 */, + unchecked((long) 0x07B43F50627A6778L) /* 218 */, unchecked((long) 0x4A44AB49F5ECCC77L) /* 219 */, + unchecked((long) 0x3BC3D6E4B679EE98L) /* 220 */, unchecked((long) 0x9CC0D4D1CF14108CL) /* 221 */, + unchecked((long) 0x4406C00B206BC8A0L) /* 222 */, unchecked((long) 0x82A18854C8D72D89L) /* 223 */, + unchecked((long) 0x67E366B35C3C432CL) /* 224 */, unchecked((long) 0xB923DD61102B37F2L) /* 225 */, + unchecked((long) 0x56AB2779D884271DL) /* 226 */, unchecked((long) 0xBE83E1B0FF1525AFL) /* 227 */, + unchecked((long) 0xFB7C65D4217E49A9L) /* 228 */, unchecked((long) 0x6BDBE0E76D48E7D4L) /* 229 */, + unchecked((long) 0x08DF828745D9179EL) /* 230 */, unchecked((long) 0x22EA6A9ADD53BD34L) /* 231 */, + unchecked((long) 0xE36E141C5622200AL) /* 232 */, unchecked((long) 0x7F805D1B8CB750EEL) /* 233 */, + unchecked((long) 0xAFE5C7A59F58E837L) /* 234 */, unchecked((long) 0xE27F996A4FB1C23CL) /* 235 */, + unchecked((long) 0xD3867DFB0775F0D0L) /* 236 */, unchecked((long) 0xD0E673DE6E88891AL) /* 237 */, + unchecked((long) 0x123AEB9EAFB86C25L) /* 238 */, unchecked((long) 0x30F1D5D5C145B895L) /* 239 */, + unchecked((long) 0xBB434A2DEE7269E7L) /* 240 */, unchecked((long) 0x78CB67ECF931FA38L) /* 241 */, + unchecked((long) 0xF33B0372323BBF9CL) /* 242 */, unchecked((long) 0x52D66336FB279C74L) /* 243 */, + unchecked((long) 0x505F33AC0AFB4EAAL) /* 244 */, unchecked((long) 0xE8A5CD99A2CCE187L) /* 245 */, + unchecked((long) 0x534974801E2D30BBL) /* 246 */, unchecked((long) 0x8D2D5711D5876D90L) /* 247 */, + unchecked((long) 0x1F1A412891BC038EL) /* 248 */, unchecked((long) 0xD6E2E71D82E56648L) /* 249 */, + unchecked((long) 0x74036C3A497732B7L) /* 250 */, unchecked((long) 0x89B67ED96361F5ABL) /* 251 */, + unchecked((long) 0xFFED95D8F1EA02A2L) /* 252 */, unchecked((long) 0xE72B3BD61464D43DL) /* 253 */, + unchecked((long) 0xA6300F170BDC4820L) /* 254 */, unchecked((long) 0xEBC18760ED78A77AL) /* 255 */, + }; + + private static readonly long[] t2 = { + unchecked((long) 0xE6A6BE5A05A12138L) /* 256 */, unchecked((long) 0xB5A122A5B4F87C98L) /* 257 */, + unchecked((long) 0x563C6089140B6990L) /* 258 */, unchecked((long) 0x4C46CB2E391F5DD5L) /* 259 */, + unchecked((long) 0xD932ADDBC9B79434L) /* 260 */, unchecked((long) 0x08EA70E42015AFF5L) /* 261 */, + unchecked((long) 0xD765A6673E478CF1L) /* 262 */, unchecked((long) 0xC4FB757EAB278D99L) /* 263 */, + unchecked((long) 0xDF11C6862D6E0692L) /* 264 */, unchecked((long) 0xDDEB84F10D7F3B16L) /* 265 */, + unchecked((long) 0x6F2EF604A665EA04L) /* 266 */, unchecked((long) 0x4A8E0F0FF0E0DFB3L) /* 267 */, + unchecked((long) 0xA5EDEEF83DBCBA51L) /* 268 */, unchecked((long) 0xFC4F0A2A0EA4371EL) /* 269 */, + unchecked((long) 0xE83E1DA85CB38429L) /* 270 */, unchecked((long) 0xDC8FF882BA1B1CE2L) /* 271 */, + unchecked((long) 0xCD45505E8353E80DL) /* 272 */, unchecked((long) 0x18D19A00D4DB0717L) /* 273 */, + unchecked((long) 0x34A0CFEDA5F38101L) /* 274 */, unchecked((long) 0x0BE77E518887CAF2L) /* 275 */, + unchecked((long) 0x1E341438B3C45136L) /* 276 */, unchecked((long) 0xE05797F49089CCF9L) /* 277 */, + unchecked((long) 0xFFD23F9DF2591D14L) /* 278 */, unchecked((long) 0x543DDA228595C5CDL) /* 279 */, + unchecked((long) 0x661F81FD99052A33L) /* 280 */, unchecked((long) 0x8736E641DB0F7B76L) /* 281 */, + unchecked((long) 0x15227725418E5307L) /* 282 */, unchecked((long) 0xE25F7F46162EB2FAL) /* 283 */, + unchecked((long) 0x48A8B2126C13D9FEL) /* 284 */, unchecked((long) 0xAFDC541792E76EEAL) /* 285 */, + unchecked((long) 0x03D912BFC6D1898FL) /* 286 */, unchecked((long) 0x31B1AAFA1B83F51BL) /* 287 */, + unchecked((long) 0xF1AC2796E42AB7D9L) /* 288 */, unchecked((long) 0x40A3A7D7FCD2EBACL) /* 289 */, + unchecked((long) 0x1056136D0AFBBCC5L) /* 290 */, unchecked((long) 0x7889E1DD9A6D0C85L) /* 291 */, + unchecked((long) 0xD33525782A7974AAL) /* 292 */, unchecked((long) 0xA7E25D09078AC09BL) /* 293 */, + unchecked((long) 0xBD4138B3EAC6EDD0L) /* 294 */, unchecked((long) 0x920ABFBE71EB9E70L) /* 295 */, + unchecked((long) 0xA2A5D0F54FC2625CL) /* 296 */, unchecked((long) 0xC054E36B0B1290A3L) /* 297 */, + unchecked((long) 0xF6DD59FF62FE932BL) /* 298 */, unchecked((long) 0x3537354511A8AC7DL) /* 299 */, + unchecked((long) 0xCA845E9172FADCD4L) /* 300 */, unchecked((long) 0x84F82B60329D20DCL) /* 301 */, + unchecked((long) 0x79C62CE1CD672F18L) /* 302 */, unchecked((long) 0x8B09A2ADD124642CL) /* 303 */, + unchecked((long) 0xD0C1E96A19D9E726L) /* 304 */, unchecked((long) 0x5A786A9B4BA9500CL) /* 305 */, + unchecked((long) 0x0E020336634C43F3L) /* 306 */, unchecked((long) 0xC17B474AEB66D822L) /* 307 */, + unchecked((long) 0x6A731AE3EC9BAAC2L) /* 308 */, unchecked((long) 0x8226667AE0840258L) /* 309 */, + unchecked((long) 0x67D4567691CAECA5L) /* 310 */, unchecked((long) 0x1D94155C4875ADB5L) /* 311 */, + unchecked((long) 0x6D00FD985B813FDFL) /* 312 */, unchecked((long) 0x51286EFCB774CD06L) /* 313 */, + unchecked((long) 0x5E8834471FA744AFL) /* 314 */, unchecked((long) 0xF72CA0AEE761AE2EL) /* 315 */, + unchecked((long) 0xBE40E4CDAEE8E09AL) /* 316 */, unchecked((long) 0xE9970BBB5118F665L) /* 317 */, + unchecked((long) 0x726E4BEB33DF1964L) /* 318 */, unchecked((long) 0x703B000729199762L) /* 319 */, + unchecked((long) 0x4631D816F5EF30A7L) /* 320 */, unchecked((long) 0xB880B5B51504A6BEL) /* 321 */, + unchecked((long) 0x641793C37ED84B6CL) /* 322 */, unchecked((long) 0x7B21ED77F6E97D96L) /* 323 */, + unchecked((long) 0x776306312EF96B73L) /* 324 */, unchecked((long) 0xAE528948E86FF3F4L) /* 325 */, + unchecked((long) 0x53DBD7F286A3F8F8L) /* 326 */, unchecked((long) 0x16CADCE74CFC1063L) /* 327 */, + unchecked((long) 0x005C19BDFA52C6DDL) /* 328 */, unchecked((long) 0x68868F5D64D46AD3L) /* 329 */, + unchecked((long) 0x3A9D512CCF1E186AL) /* 330 */, unchecked((long) 0x367E62C2385660AEL) /* 331 */, + unchecked((long) 0xE359E7EA77DCB1D7L) /* 332 */, unchecked((long) 0x526C0773749ABE6EL) /* 333 */, + unchecked((long) 0x735AE5F9D09F734BL) /* 334 */, unchecked((long) 0x493FC7CC8A558BA8L) /* 335 */, + unchecked((long) 0xB0B9C1533041AB45L) /* 336 */, unchecked((long) 0x321958BA470A59BDL) /* 337 */, + unchecked((long) 0x852DB00B5F46C393L) /* 338 */, unchecked((long) 0x91209B2BD336B0E5L) /* 339 */, + unchecked((long) 0x6E604F7D659EF19FL) /* 340 */, unchecked((long) 0xB99A8AE2782CCB24L) /* 341 */, + unchecked((long) 0xCCF52AB6C814C4C7L) /* 342 */, unchecked((long) 0x4727D9AFBE11727BL) /* 343 */, + unchecked((long) 0x7E950D0C0121B34DL) /* 344 */, unchecked((long) 0x756F435670AD471FL) /* 345 */, + unchecked((long) 0xF5ADD442615A6849L) /* 346 */, unchecked((long) 0x4E87E09980B9957AL) /* 347 */, + unchecked((long) 0x2ACFA1DF50AEE355L) /* 348 */, unchecked((long) 0xD898263AFD2FD556L) /* 349 */, + unchecked((long) 0xC8F4924DD80C8FD6L) /* 350 */, unchecked((long) 0xCF99CA3D754A173AL) /* 351 */, + unchecked((long) 0xFE477BACAF91BF3CL) /* 352 */, unchecked((long) 0xED5371F6D690C12DL) /* 353 */, + unchecked((long) 0x831A5C285E687094L) /* 354 */, unchecked((long) 0xC5D3C90A3708A0A4L) /* 355 */, + unchecked((long) 0x0F7F903717D06580L) /* 356 */, unchecked((long) 0x19F9BB13B8FDF27FL) /* 357 */, + unchecked((long) 0xB1BD6F1B4D502843L) /* 358 */, unchecked((long) 0x1C761BA38FFF4012L) /* 359 */, + unchecked((long) 0x0D1530C4E2E21F3BL) /* 360 */, unchecked((long) 0x8943CE69A7372C8AL) /* 361 */, + unchecked((long) 0xE5184E11FEB5CE66L) /* 362 */, unchecked((long) 0x618BDB80BD736621L) /* 363 */, + unchecked((long) 0x7D29BAD68B574D0BL) /* 364 */, unchecked((long) 0x81BB613E25E6FE5BL) /* 365 */, + unchecked((long) 0x071C9C10BC07913FL) /* 366 */, unchecked((long) 0xC7BEEB7909AC2D97L) /* 367 */, + unchecked((long) 0xC3E58D353BC5D757L) /* 368 */, unchecked((long) 0xEB017892F38F61E8L) /* 369 */, + unchecked((long) 0xD4EFFB9C9B1CC21AL) /* 370 */, unchecked((long) 0x99727D26F494F7ABL) /* 371 */, + unchecked((long) 0xA3E063A2956B3E03L) /* 372 */, unchecked((long) 0x9D4A8B9A4AA09C30L) /* 373 */, + unchecked((long) 0x3F6AB7D500090FB4L) /* 374 */, unchecked((long) 0x9CC0F2A057268AC0L) /* 375 */, + unchecked((long) 0x3DEE9D2DEDBF42D1L) /* 376 */, unchecked((long) 0x330F49C87960A972L) /* 377 */, + unchecked((long) 0xC6B2720287421B41L) /* 378 */, unchecked((long) 0x0AC59EC07C00369CL) /* 379 */, + unchecked((long) 0xEF4EAC49CB353425L) /* 380 */, unchecked((long) 0xF450244EEF0129D8L) /* 381 */, + unchecked((long) 0x8ACC46E5CAF4DEB6L) /* 382 */, unchecked((long) 0x2FFEAB63989263F7L) /* 383 */, + unchecked((long) 0x8F7CB9FE5D7A4578L) /* 384 */, unchecked((long) 0x5BD8F7644E634635L) /* 385 */, + unchecked((long) 0x427A7315BF2DC900L) /* 386 */, unchecked((long) 0x17D0C4AA2125261CL) /* 387 */, + unchecked((long) 0x3992486C93518E50L) /* 388 */, unchecked((long) 0xB4CBFEE0A2D7D4C3L) /* 389 */, + unchecked((long) 0x7C75D6202C5DDD8DL) /* 390 */, unchecked((long) 0xDBC295D8E35B6C61L) /* 391 */, + unchecked((long) 0x60B369D302032B19L) /* 392 */, unchecked((long) 0xCE42685FDCE44132L) /* 393 */, + unchecked((long) 0x06F3DDB9DDF65610L) /* 394 */, unchecked((long) 0x8EA4D21DB5E148F0L) /* 395 */, + unchecked((long) 0x20B0FCE62FCD496FL) /* 396 */, unchecked((long) 0x2C1B912358B0EE31L) /* 397 */, + unchecked((long) 0xB28317B818F5A308L) /* 398 */, unchecked((long) 0xA89C1E189CA6D2CFL) /* 399 */, + unchecked((long) 0x0C6B18576AAADBC8L) /* 400 */, unchecked((long) 0xB65DEAA91299FAE3L) /* 401 */, + unchecked((long) 0xFB2B794B7F1027E7L) /* 402 */, unchecked((long) 0x04E4317F443B5BEBL) /* 403 */, + unchecked((long) 0x4B852D325939D0A6L) /* 404 */, unchecked((long) 0xD5AE6BEEFB207FFCL) /* 405 */, + unchecked((long) 0x309682B281C7D374L) /* 406 */, unchecked((long) 0xBAE309A194C3B475L) /* 407 */, + unchecked((long) 0x8CC3F97B13B49F05L) /* 408 */, unchecked((long) 0x98A9422FF8293967L) /* 409 */, + unchecked((long) 0x244B16B01076FF7CL) /* 410 */, unchecked((long) 0xF8BF571C663D67EEL) /* 411 */, + unchecked((long) 0x1F0D6758EEE30DA1L) /* 412 */, unchecked((long) 0xC9B611D97ADEB9B7L) /* 413 */, + unchecked((long) 0xB7AFD5887B6C57A2L) /* 414 */, unchecked((long) 0x6290AE846B984FE1L) /* 415 */, + unchecked((long) 0x94DF4CDEACC1A5FDL) /* 416 */, unchecked((long) 0x058A5BD1C5483AFFL) /* 417 */, + unchecked((long) 0x63166CC142BA3C37L) /* 418 */, unchecked((long) 0x8DB8526EB2F76F40L) /* 419 */, + unchecked((long) 0xE10880036F0D6D4EL) /* 420 */, unchecked((long) 0x9E0523C9971D311DL) /* 421 */, + unchecked((long) 0x45EC2824CC7CD691L) /* 422 */, unchecked((long) 0x575B8359E62382C9L) /* 423 */, + unchecked((long) 0xFA9E400DC4889995L) /* 424 */, unchecked((long) 0xD1823ECB45721568L) /* 425 */, + unchecked((long) 0xDAFD983B8206082FL) /* 426 */, unchecked((long) 0xAA7D29082386A8CBL) /* 427 */, + unchecked((long) 0x269FCD4403B87588L) /* 428 */, unchecked((long) 0x1B91F5F728BDD1E0L) /* 429 */, + unchecked((long) 0xE4669F39040201F6L) /* 430 */, unchecked((long) 0x7A1D7C218CF04ADEL) /* 431 */, + unchecked((long) 0x65623C29D79CE5CEL) /* 432 */, unchecked((long) 0x2368449096C00BB1L) /* 433 */, + unchecked((long) 0xAB9BF1879DA503BAL) /* 434 */, unchecked((long) 0xBC23ECB1A458058EL) /* 435 */, + unchecked((long) 0x9A58DF01BB401ECCL) /* 436 */, unchecked((long) 0xA070E868A85F143DL) /* 437 */, + unchecked((long) 0x4FF188307DF2239EL) /* 438 */, unchecked((long) 0x14D565B41A641183L) /* 439 */, + unchecked((long) 0xEE13337452701602L) /* 440 */, unchecked((long) 0x950E3DCF3F285E09L) /* 441 */, + unchecked((long) 0x59930254B9C80953L) /* 442 */, unchecked((long) 0x3BF299408930DA6DL) /* 443 */, + unchecked((long) 0xA955943F53691387L) /* 444 */, unchecked((long) 0xA15EDECAA9CB8784L) /* 445 */, + unchecked((long) 0x29142127352BE9A0L) /* 446 */, unchecked((long) 0x76F0371FFF4E7AFBL) /* 447 */, + unchecked((long) 0x0239F450274F2228L) /* 448 */, unchecked((long) 0xBB073AF01D5E868BL) /* 449 */, + unchecked((long) 0xBFC80571C10E96C1L) /* 450 */, unchecked((long) 0xD267088568222E23L) /* 451 */, + unchecked((long) 0x9671A3D48E80B5B0L) /* 452 */, unchecked((long) 0x55B5D38AE193BB81L) /* 453 */, + unchecked((long) 0x693AE2D0A18B04B8L) /* 454 */, unchecked((long) 0x5C48B4ECADD5335FL) /* 455 */, + unchecked((long) 0xFD743B194916A1CAL) /* 456 */, unchecked((long) 0x2577018134BE98C4L) /* 457 */, + unchecked((long) 0xE77987E83C54A4ADL) /* 458 */, unchecked((long) 0x28E11014DA33E1B9L) /* 459 */, + unchecked((long) 0x270CC59E226AA213L) /* 460 */, unchecked((long) 0x71495F756D1A5F60L) /* 461 */, + unchecked((long) 0x9BE853FB60AFEF77L) /* 462 */, unchecked((long) 0xADC786A7F7443DBFL) /* 463 */, + unchecked((long) 0x0904456173B29A82L) /* 464 */, unchecked((long) 0x58BC7A66C232BD5EL) /* 465 */, + unchecked((long) 0xF306558C673AC8B2L) /* 466 */, unchecked((long) 0x41F639C6B6C9772AL) /* 467 */, + unchecked((long) 0x216DEFE99FDA35DAL) /* 468 */, unchecked((long) 0x11640CC71C7BE615L) /* 469 */, + unchecked((long) 0x93C43694565C5527L) /* 470 */, unchecked((long) 0xEA038E6246777839L) /* 471 */, + unchecked((long) 0xF9ABF3CE5A3E2469L) /* 472 */, unchecked((long) 0x741E768D0FD312D2L) /* 473 */, + unchecked((long) 0x0144B883CED652C6L) /* 474 */, unchecked((long) 0xC20B5A5BA33F8552L) /* 475 */, + unchecked((long) 0x1AE69633C3435A9DL) /* 476 */, unchecked((long) 0x97A28CA4088CFDECL) /* 477 */, + unchecked((long) 0x8824A43C1E96F420L) /* 478 */, unchecked((long) 0x37612FA66EEEA746L) /* 479 */, + unchecked((long) 0x6B4CB165F9CF0E5AL) /* 480 */, unchecked((long) 0x43AA1C06A0ABFB4AL) /* 481 */, + unchecked((long) 0x7F4DC26FF162796BL) /* 482 */, unchecked((long) 0x6CBACC8E54ED9B0FL) /* 483 */, + unchecked((long) 0xA6B7FFEFD2BB253EL) /* 484 */, unchecked((long) 0x2E25BC95B0A29D4FL) /* 485 */, + unchecked((long) 0x86D6A58BDEF1388CL) /* 486 */, unchecked((long) 0xDED74AC576B6F054L) /* 487 */, + unchecked((long) 0x8030BDBC2B45805DL) /* 488 */, unchecked((long) 0x3C81AF70E94D9289L) /* 489 */, + unchecked((long) 0x3EFF6DDA9E3100DBL) /* 490 */, unchecked((long) 0xB38DC39FDFCC8847L) /* 491 */, + unchecked((long) 0x123885528D17B87EL) /* 492 */, unchecked((long) 0xF2DA0ED240B1B642L) /* 493 */, + unchecked((long) 0x44CEFADCD54BF9A9L) /* 494 */, unchecked((long) 0x1312200E433C7EE6L) /* 495 */, + unchecked((long) 0x9FFCC84F3A78C748L) /* 496 */, unchecked((long) 0xF0CD1F72248576BBL) /* 497 */, + unchecked((long) 0xEC6974053638CFE4L) /* 498 */, unchecked((long) 0x2BA7B67C0CEC4E4CL) /* 499 */, + unchecked((long) 0xAC2F4DF3E5CE32EDL) /* 500 */, unchecked((long) 0xCB33D14326EA4C11L) /* 501 */, + unchecked((long) 0xA4E9044CC77E58BCL) /* 502 */, unchecked((long) 0x5F513293D934FCEFL) /* 503 */, + unchecked((long) 0x5DC9645506E55444L) /* 504 */, unchecked((long) 0x50DE418F317DE40AL) /* 505 */, + unchecked((long) 0x388CB31A69DDE259L) /* 506 */, unchecked((long) 0x2DB4A83455820A86L) /* 507 */, + unchecked((long) 0x9010A91E84711AE9L) /* 508 */, unchecked((long) 0x4DF7F0B7B1498371L) /* 509 */, + unchecked((long) 0xD62A2EABC0977179L) /* 510 */, unchecked((long) 0x22FAC097AA8D5C0EL) /* 511 */, + }; + + private static readonly long[] t3 = { + unchecked((long) 0xF49FCC2FF1DAF39BL) /* 512 */, unchecked((long) 0x487FD5C66FF29281L) /* 513 */, + unchecked((long) 0xE8A30667FCDCA83FL) /* 514 */, unchecked((long) 0x2C9B4BE3D2FCCE63L) /* 515 */, + unchecked((long) 0xDA3FF74B93FBBBC2L) /* 516 */, unchecked((long) 0x2FA165D2FE70BA66L) /* 517 */, + unchecked((long) 0xA103E279970E93D4L) /* 518 */, unchecked((long) 0xBECDEC77B0E45E71L) /* 519 */, + unchecked((long) 0xCFB41E723985E497L) /* 520 */, unchecked((long) 0xB70AAA025EF75017L) /* 521 */, + unchecked((long) 0xD42309F03840B8E0L) /* 522 */, unchecked((long) 0x8EFC1AD035898579L) /* 523 */, + unchecked((long) 0x96C6920BE2B2ABC5L) /* 524 */, unchecked((long) 0x66AF4163375A9172L) /* 525 */, + unchecked((long) 0x2174ABDCCA7127FBL) /* 526 */, unchecked((long) 0xB33CCEA64A72FF41L) /* 527 */, + unchecked((long) 0xF04A4933083066A5L) /* 528 */, unchecked((long) 0x8D970ACDD7289AF5L) /* 529 */, + unchecked((long) 0x8F96E8E031C8C25EL) /* 530 */, unchecked((long) 0xF3FEC02276875D47L) /* 531 */, + unchecked((long) 0xEC7BF310056190DDL) /* 532 */, unchecked((long) 0xF5ADB0AEBB0F1491L) /* 533 */, + unchecked((long) 0x9B50F8850FD58892L) /* 534 */, unchecked((long) 0x4975488358B74DE8L) /* 535 */, + unchecked((long) 0xA3354FF691531C61L) /* 536 */, unchecked((long) 0x0702BBE481D2C6EEL) /* 537 */, + unchecked((long) 0x89FB24057DEDED98L) /* 538 */, unchecked((long) 0xAC3075138596E902L) /* 539 */, + unchecked((long) 0x1D2D3580172772EDL) /* 540 */, unchecked((long) 0xEB738FC28E6BC30DL) /* 541 */, + unchecked((long) 0x5854EF8F63044326L) /* 542 */, unchecked((long) 0x9E5C52325ADD3BBEL) /* 543 */, + unchecked((long) 0x90AA53CF325C4623L) /* 544 */, unchecked((long) 0xC1D24D51349DD067L) /* 545 */, + unchecked((long) 0x2051CFEEA69EA624L) /* 546 */, unchecked((long) 0x13220F0A862E7E4FL) /* 547 */, + unchecked((long) 0xCE39399404E04864L) /* 548 */, unchecked((long) 0xD9C42CA47086FCB7L) /* 549 */, + unchecked((long) 0x685AD2238A03E7CCL) /* 550 */, unchecked((long) 0x066484B2AB2FF1DBL) /* 551 */, + unchecked((long) 0xFE9D5D70EFBF79ECL) /* 552 */, unchecked((long) 0x5B13B9DD9C481854L) /* 553 */, + unchecked((long) 0x15F0D475ED1509ADL) /* 554 */, unchecked((long) 0x0BEBCD060EC79851L) /* 555 */, + unchecked((long) 0xD58C6791183AB7F8L) /* 556 */, unchecked((long) 0xD1187C5052F3EEE4L) /* 557 */, + unchecked((long) 0xC95D1192E54E82FFL) /* 558 */, unchecked((long) 0x86EEA14CB9AC6CA2L) /* 559 */, + unchecked((long) 0x3485BEB153677D5DL) /* 560 */, unchecked((long) 0xDD191D781F8C492AL) /* 561 */, + unchecked((long) 0xF60866BAA784EBF9L) /* 562 */, unchecked((long) 0x518F643BA2D08C74L) /* 563 */, + unchecked((long) 0x8852E956E1087C22L) /* 564 */, unchecked((long) 0xA768CB8DC410AE8DL) /* 565 */, + unchecked((long) 0x38047726BFEC8E1AL) /* 566 */, unchecked((long) 0xA67738B4CD3B45AAL) /* 567 */, + unchecked((long) 0xAD16691CEC0DDE19L) /* 568 */, unchecked((long) 0xC6D4319380462E07L) /* 569 */, + unchecked((long) 0xC5A5876D0BA61938L) /* 570 */, unchecked((long) 0x16B9FA1FA58FD840L) /* 571 */, + unchecked((long) 0x188AB1173CA74F18L) /* 572 */, unchecked((long) 0xABDA2F98C99C021FL) /* 573 */, + unchecked((long) 0x3E0580AB134AE816L) /* 574 */, unchecked((long) 0x5F3B05B773645ABBL) /* 575 */, + unchecked((long) 0x2501A2BE5575F2F6L) /* 576 */, unchecked((long) 0x1B2F74004E7E8BA9L) /* 577 */, + unchecked((long) 0x1CD7580371E8D953L) /* 578 */, unchecked((long) 0x7F6ED89562764E30L) /* 579 */, + unchecked((long) 0xB15926FF596F003DL) /* 580 */, unchecked((long) 0x9F65293DA8C5D6B9L) /* 581 */, + unchecked((long) 0x6ECEF04DD690F84CL) /* 582 */, unchecked((long) 0x4782275FFF33AF88L) /* 583 */, + unchecked((long) 0xE41433083F820801L) /* 584 */, unchecked((long) 0xFD0DFE409A1AF9B5L) /* 585 */, + unchecked((long) 0x4325A3342CDB396BL) /* 586 */, unchecked((long) 0x8AE77E62B301B252L) /* 587 */, + unchecked((long) 0xC36F9E9F6655615AL) /* 588 */, unchecked((long) 0x85455A2D92D32C09L) /* 589 */, + unchecked((long) 0xF2C7DEA949477485L) /* 590 */, unchecked((long) 0x63CFB4C133A39EBAL) /* 591 */, + unchecked((long) 0x83B040CC6EBC5462L) /* 592 */, unchecked((long) 0x3B9454C8FDB326B0L) /* 593 */, + unchecked((long) 0x56F56A9E87FFD78CL) /* 594 */, unchecked((long) 0x2DC2940D99F42BC6L) /* 595 */, + unchecked((long) 0x98F7DF096B096E2DL) /* 596 */, unchecked((long) 0x19A6E01E3AD852BFL) /* 597 */, + unchecked((long) 0x42A99CCBDBD4B40BL) /* 598 */, unchecked((long) 0xA59998AF45E9C559L) /* 599 */, + unchecked((long) 0x366295E807D93186L) /* 600 */, unchecked((long) 0x6B48181BFAA1F773L) /* 601 */, + unchecked((long) 0x1FEC57E2157A0A1DL) /* 602 */, unchecked((long) 0x4667446AF6201AD5L) /* 603 */, + unchecked((long) 0xE615EBCACFB0F075L) /* 604 */, unchecked((long) 0xB8F31F4F68290778L) /* 605 */, + unchecked((long) 0x22713ED6CE22D11EL) /* 606 */, unchecked((long) 0x3057C1A72EC3C93BL) /* 607 */, + unchecked((long) 0xCB46ACC37C3F1F2FL) /* 608 */, unchecked((long) 0xDBB893FD02AAF50EL) /* 609 */, + unchecked((long) 0x331FD92E600B9FCFL) /* 610 */, unchecked((long) 0xA498F96148EA3AD6L) /* 611 */, + unchecked((long) 0xA8D8426E8B6A83EAL) /* 612 */, unchecked((long) 0xA089B274B7735CDCL) /* 613 */, + unchecked((long) 0x87F6B3731E524A11L) /* 614 */, unchecked((long) 0x118808E5CBC96749L) /* 615 */, + unchecked((long) 0x9906E4C7B19BD394L) /* 616 */, unchecked((long) 0xAFED7F7E9B24A20CL) /* 617 */, + unchecked((long) 0x6509EADEEB3644A7L) /* 618 */, unchecked((long) 0x6C1EF1D3E8EF0EDEL) /* 619 */, + unchecked((long) 0xB9C97D43E9798FB4L) /* 620 */, unchecked((long) 0xA2F2D784740C28A3L) /* 621 */, + unchecked((long) 0x7B8496476197566FL) /* 622 */, unchecked((long) 0x7A5BE3E6B65F069DL) /* 623 */, + unchecked((long) 0xF96330ED78BE6F10L) /* 624 */, unchecked((long) 0xEEE60DE77A076A15L) /* 625 */, + unchecked((long) 0x2B4BEE4AA08B9BD0L) /* 626 */, unchecked((long) 0x6A56A63EC7B8894EL) /* 627 */, + unchecked((long) 0x02121359BA34FEF4L) /* 628 */, unchecked((long) 0x4CBF99F8283703FCL) /* 629 */, + unchecked((long) 0x398071350CAF30C8L) /* 630 */, unchecked((long) 0xD0A77A89F017687AL) /* 631 */, + unchecked((long) 0xF1C1A9EB9E423569L) /* 632 */, unchecked((long) 0x8C7976282DEE8199L) /* 633 */, + unchecked((long) 0x5D1737A5DD1F7ABDL) /* 634 */, unchecked((long) 0x4F53433C09A9FA80L) /* 635 */, + unchecked((long) 0xFA8B0C53DF7CA1D9L) /* 636 */, unchecked((long) 0x3FD9DCBC886CCB77L) /* 637 */, + unchecked((long) 0xC040917CA91B4720L) /* 638 */, unchecked((long) 0x7DD00142F9D1DCDFL) /* 639 */, + unchecked((long) 0x8476FC1D4F387B58L) /* 640 */, unchecked((long) 0x23F8E7C5F3316503L) /* 641 */, + unchecked((long) 0x032A2244E7E37339L) /* 642 */, unchecked((long) 0x5C87A5D750F5A74BL) /* 643 */, + unchecked((long) 0x082B4CC43698992EL) /* 644 */, unchecked((long) 0xDF917BECB858F63CL) /* 645 */, + unchecked((long) 0x3270B8FC5BF86DDAL) /* 646 */, unchecked((long) 0x10AE72BB29B5DD76L) /* 647 */, + unchecked((long) 0x576AC94E7700362BL) /* 648 */, unchecked((long) 0x1AD112DAC61EFB8FL) /* 649 */, + unchecked((long) 0x691BC30EC5FAA427L) /* 650 */, unchecked((long) 0xFF246311CC327143L) /* 651 */, + unchecked((long) 0x3142368E30E53206L) /* 652 */, unchecked((long) 0x71380E31E02CA396L) /* 653 */, + unchecked((long) 0x958D5C960AAD76F1L) /* 654 */, unchecked((long) 0xF8D6F430C16DA536L) /* 655 */, + unchecked((long) 0xC8FFD13F1BE7E1D2L) /* 656 */, unchecked((long) 0x7578AE66004DDBE1L) /* 657 */, + unchecked((long) 0x05833F01067BE646L) /* 658 */, unchecked((long) 0xBB34B5AD3BFE586DL) /* 659 */, + unchecked((long) 0x095F34C9A12B97F0L) /* 660 */, unchecked((long) 0x247AB64525D60CA8L) /* 661 */, + unchecked((long) 0xDCDBC6F3017477D1L) /* 662 */, unchecked((long) 0x4A2E14D4DECAD24DL) /* 663 */, + unchecked((long) 0xBDB5E6D9BE0A1EEBL) /* 664 */, unchecked((long) 0x2A7E70F7794301ABL) /* 665 */, + unchecked((long) 0xDEF42D8A270540FDL) /* 666 */, unchecked((long) 0x01078EC0A34C22C1L) /* 667 */, + unchecked((long) 0xE5DE511AF4C16387L) /* 668 */, unchecked((long) 0x7EBB3A52BD9A330AL) /* 669 */, + unchecked((long) 0x77697857AA7D6435L) /* 670 */, unchecked((long) 0x004E831603AE4C32L) /* 671 */, + unchecked((long) 0xE7A21020AD78E312L) /* 672 */, unchecked((long) 0x9D41A70C6AB420F2L) /* 673 */, + unchecked((long) 0x28E06C18EA1141E6L) /* 674 */, unchecked((long) 0xD2B28CBD984F6B28L) /* 675 */, + unchecked((long) 0x26B75F6C446E9D83L) /* 676 */, unchecked((long) 0xBA47568C4D418D7FL) /* 677 */, + unchecked((long) 0xD80BADBFE6183D8EL) /* 678 */, unchecked((long) 0x0E206D7F5F166044L) /* 679 */, + unchecked((long) 0xE258A43911CBCA3EL) /* 680 */, unchecked((long) 0x723A1746B21DC0BCL) /* 681 */, + unchecked((long) 0xC7CAA854F5D7CDD3L) /* 682 */, unchecked((long) 0x7CAC32883D261D9CL) /* 683 */, + unchecked((long) 0x7690C26423BA942CL) /* 684 */, unchecked((long) 0x17E55524478042B8L) /* 685 */, + unchecked((long) 0xE0BE477656A2389FL) /* 686 */, unchecked((long) 0x4D289B5E67AB2DA0L) /* 687 */, + unchecked((long) 0x44862B9C8FBBFD31L) /* 688 */, unchecked((long) 0xB47CC8049D141365L) /* 689 */, + unchecked((long) 0x822C1B362B91C793L) /* 690 */, unchecked((long) 0x4EB14655FB13DFD8L) /* 691 */, + unchecked((long) 0x1ECBBA0714E2A97BL) /* 692 */, unchecked((long) 0x6143459D5CDE5F14L) /* 693 */, + unchecked((long) 0x53A8FBF1D5F0AC89L) /* 694 */, unchecked((long) 0x97EA04D81C5E5B00L) /* 695 */, + unchecked((long) 0x622181A8D4FDB3F3L) /* 696 */, unchecked((long) 0xE9BCD341572A1208L) /* 697 */, + unchecked((long) 0x1411258643CCE58AL) /* 698 */, unchecked((long) 0x9144C5FEA4C6E0A4L) /* 699 */, + unchecked((long) 0x0D33D06565CF620FL) /* 700 */, unchecked((long) 0x54A48D489F219CA1L) /* 701 */, + unchecked((long) 0xC43E5EAC6D63C821L) /* 702 */, unchecked((long) 0xA9728B3A72770DAFL) /* 703 */, + unchecked((long) 0xD7934E7B20DF87EFL) /* 704 */, unchecked((long) 0xE35503B61A3E86E5L) /* 705 */, + unchecked((long) 0xCAE321FBC819D504L) /* 706 */, unchecked((long) 0x129A50B3AC60BFA6L) /* 707 */, + unchecked((long) 0xCD5E68EA7E9FB6C3L) /* 708 */, unchecked((long) 0xB01C90199483B1C7L) /* 709 */, + unchecked((long) 0x3DE93CD5C295376CL) /* 710 */, unchecked((long) 0xAED52EDF2AB9AD13L) /* 711 */, + unchecked((long) 0x2E60F512C0A07884L) /* 712 */, unchecked((long) 0xBC3D86A3E36210C9L) /* 713 */, + unchecked((long) 0x35269D9B163951CEL) /* 714 */, unchecked((long) 0x0C7D6E2AD0CDB5FAL) /* 715 */, + unchecked((long) 0x59E86297D87F5733L) /* 716 */, unchecked((long) 0x298EF221898DB0E7L) /* 717 */, + unchecked((long) 0x55000029D1A5AA7EL) /* 718 */, unchecked((long) 0x8BC08AE1B5061B45L) /* 719 */, + unchecked((long) 0xC2C31C2B6C92703AL) /* 720 */, unchecked((long) 0x94CC596BAF25EF42L) /* 721 */, + unchecked((long) 0x0A1D73DB22540456L) /* 722 */, unchecked((long) 0x04B6A0F9D9C4179AL) /* 723 */, + unchecked((long) 0xEFFDAFA2AE3D3C60L) /* 724 */, unchecked((long) 0xF7C8075BB49496C4L) /* 725 */, + unchecked((long) 0x9CC5C7141D1CD4E3L) /* 726 */, unchecked((long) 0x78BD1638218E5534L) /* 727 */, + unchecked((long) 0xB2F11568F850246AL) /* 728 */, unchecked((long) 0xEDFABCFA9502BC29L) /* 729 */, + unchecked((long) 0x796CE5F2DA23051BL) /* 730 */, unchecked((long) 0xAAE128B0DC93537CL) /* 731 */, + unchecked((long) 0x3A493DA0EE4B29AEL) /* 732 */, unchecked((long) 0xB5DF6B2C416895D7L) /* 733 */, + unchecked((long) 0xFCABBD25122D7F37L) /* 734 */, unchecked((long) 0x70810B58105DC4B1L) /* 735 */, + unchecked((long) 0xE10FDD37F7882A90L) /* 736 */, unchecked((long) 0x524DCAB5518A3F5CL) /* 737 */, + unchecked((long) 0x3C9E85878451255BL) /* 738 */, unchecked((long) 0x4029828119BD34E2L) /* 739 */, + unchecked((long) 0x74A05B6F5D3CECCBL) /* 740 */, unchecked((long) 0xB610021542E13ECAL) /* 741 */, + unchecked((long) 0x0FF979D12F59E2ACL) /* 742 */, unchecked((long) 0x6037DA27E4F9CC50L) /* 743 */, + unchecked((long) 0x5E92975A0DF1847DL) /* 744 */, unchecked((long) 0xD66DE190D3E623FEL) /* 745 */, + unchecked((long) 0x5032D6B87B568048L) /* 746 */, unchecked((long) 0x9A36B7CE8235216EL) /* 747 */, + unchecked((long) 0x80272A7A24F64B4AL) /* 748 */, unchecked((long) 0x93EFED8B8C6916F7L) /* 749 */, + unchecked((long) 0x37DDBFF44CCE1555L) /* 750 */, unchecked((long) 0x4B95DB5D4B99BD25L) /* 751 */, + unchecked((long) 0x92D3FDA169812FC0L) /* 752 */, unchecked((long) 0xFB1A4A9A90660BB6L) /* 753 */, + unchecked((long) 0x730C196946A4B9B2L) /* 754 */, unchecked((long) 0x81E289AA7F49DA68L) /* 755 */, + unchecked((long) 0x64669A0F83B1A05FL) /* 756 */, unchecked((long) 0x27B3FF7D9644F48BL) /* 757 */, + unchecked((long) 0xCC6B615C8DB675B3L) /* 758 */, unchecked((long) 0x674F20B9BCEBBE95L) /* 759 */, + unchecked((long) 0x6F31238275655982L) /* 760 */, unchecked((long) 0x5AE488713E45CF05L) /* 761 */, + unchecked((long) 0xBF619F9954C21157L) /* 762 */, unchecked((long) 0xEABAC46040A8EAE9L) /* 763 */, + unchecked((long) 0x454C6FE9F2C0C1CDL) /* 764 */, unchecked((long) 0x419CF6496412691CL) /* 765 */, + unchecked((long) 0xD3DC3BEF265B0F70L) /* 766 */, unchecked((long) 0x6D0E60F5C3578A9EL) /* 767 */, + }; + + private static readonly long[] t4 = { + unchecked((long) 0x5B0E608526323C55L) /* 768 */, unchecked((long) 0x1A46C1A9FA1B59F5L) /* 769 */, + unchecked((long) 0xA9E245A17C4C8FFAL) /* 770 */, unchecked((long) 0x65CA5159DB2955D7L) /* 771 */, + unchecked((long) 0x05DB0A76CE35AFC2L) /* 772 */, unchecked((long) 0x81EAC77EA9113D45L) /* 773 */, + unchecked((long) 0x528EF88AB6AC0A0DL) /* 774 */, unchecked((long) 0xA09EA253597BE3FFL) /* 775 */, + unchecked((long) 0x430DDFB3AC48CD56L) /* 776 */, unchecked((long) 0xC4B3A67AF45CE46FL) /* 777 */, + unchecked((long) 0x4ECECFD8FBE2D05EL) /* 778 */, unchecked((long) 0x3EF56F10B39935F0L) /* 779 */, + unchecked((long) 0x0B22D6829CD619C6L) /* 780 */, unchecked((long) 0x17FD460A74DF2069L) /* 781 */, + unchecked((long) 0x6CF8CC8E8510ED40L) /* 782 */, unchecked((long) 0xD6C824BF3A6ECAA7L) /* 783 */, + unchecked((long) 0x61243D581A817049L) /* 784 */, unchecked((long) 0x048BACB6BBC163A2L) /* 785 */, + unchecked((long) 0xD9A38AC27D44CC32L) /* 786 */, unchecked((long) 0x7FDDFF5BAAF410ABL) /* 787 */, + unchecked((long) 0xAD6D495AA804824BL) /* 788 */, unchecked((long) 0xE1A6A74F2D8C9F94L) /* 789 */, + unchecked((long) 0xD4F7851235DEE8E3L) /* 790 */, unchecked((long) 0xFD4B7F886540D893L) /* 791 */, + unchecked((long) 0x247C20042AA4BFDAL) /* 792 */, unchecked((long) 0x096EA1C517D1327CL) /* 793 */, + unchecked((long) 0xD56966B4361A6685L) /* 794 */, unchecked((long) 0x277DA5C31221057DL) /* 795 */, + unchecked((long) 0x94D59893A43ACFF7L) /* 796 */, unchecked((long) 0x64F0C51CCDC02281L) /* 797 */, + unchecked((long) 0x3D33BCC4FF6189DBL) /* 798 */, unchecked((long) 0xE005CB184CE66AF1L) /* 799 */, + unchecked((long) 0xFF5CCD1D1DB99BEAL) /* 800 */, unchecked((long) 0xB0B854A7FE42980FL) /* 801 */, + unchecked((long) 0x7BD46A6A718D4B9FL) /* 802 */, unchecked((long) 0xD10FA8CC22A5FD8CL) /* 803 */, + unchecked((long) 0xD31484952BE4BD31L) /* 804 */, unchecked((long) 0xC7FA975FCB243847L) /* 805 */, + unchecked((long) 0x4886ED1E5846C407L) /* 806 */, unchecked((long) 0x28CDDB791EB70B04L) /* 807 */, + unchecked((long) 0xC2B00BE2F573417FL) /* 808 */, unchecked((long) 0x5C9590452180F877L) /* 809 */, + unchecked((long) 0x7A6BDDFFF370EB00L) /* 810 */, unchecked((long) 0xCE509E38D6D9D6A4L) /* 811 */, + unchecked((long) 0xEBEB0F00647FA702L) /* 812 */, unchecked((long) 0x1DCC06CF76606F06L) /* 813 */, + unchecked((long) 0xE4D9F28BA286FF0AL) /* 814 */, unchecked((long) 0xD85A305DC918C262L) /* 815 */, + unchecked((long) 0x475B1D8732225F54L) /* 816 */, unchecked((long) 0x2D4FB51668CCB5FEL) /* 817 */, + unchecked((long) 0xA679B9D9D72BBA20L) /* 818 */, unchecked((long) 0x53841C0D912D43A5L) /* 819 */, + unchecked((long) 0x3B7EAA48BF12A4E8L) /* 820 */, unchecked((long) 0x781E0E47F22F1DDFL) /* 821 */, + unchecked((long) 0xEFF20CE60AB50973L) /* 822 */, unchecked((long) 0x20D261D19DFFB742L) /* 823 */, + unchecked((long) 0x16A12B03062A2E39L) /* 824 */, unchecked((long) 0x1960EB2239650495L) /* 825 */, + unchecked((long) 0x251C16FED50EB8B8L) /* 826 */, unchecked((long) 0x9AC0C330F826016EL) /* 827 */, + unchecked((long) 0xED152665953E7671L) /* 828 */, unchecked((long) 0x02D63194A6369570L) /* 829 */, + unchecked((long) 0x5074F08394B1C987L) /* 830 */, unchecked((long) 0x70BA598C90B25CE1L) /* 831 */, + unchecked((long) 0x794A15810B9742F6L) /* 832 */, unchecked((long) 0x0D5925E9FCAF8C6CL) /* 833 */, + unchecked((long) 0x3067716CD868744EL) /* 834 */, unchecked((long) 0x910AB077E8D7731BL) /* 835 */, + unchecked((long) 0x6A61BBDB5AC42F61L) /* 836 */, unchecked((long) 0x93513EFBF0851567L) /* 837 */, + unchecked((long) 0xF494724B9E83E9D5L) /* 838 */, unchecked((long) 0xE887E1985C09648DL) /* 839 */, + unchecked((long) 0x34B1D3C675370CFDL) /* 840 */, unchecked((long) 0xDC35E433BC0D255DL) /* 841 */, + unchecked((long) 0xD0AAB84234131BE0L) /* 842 */, unchecked((long) 0x08042A50B48B7EAFL) /* 843 */, + unchecked((long) 0x9997C4EE44A3AB35L) /* 844 */, unchecked((long) 0x829A7B49201799D0L) /* 845 */, + unchecked((long) 0x263B8307B7C54441L) /* 846 */, unchecked((long) 0x752F95F4FD6A6CA6L) /* 847 */, + unchecked((long) 0x927217402C08C6E5L) /* 848 */, unchecked((long) 0x2A8AB754A795D9EEL) /* 849 */, + unchecked((long) 0xA442F7552F72943DL) /* 850 */, unchecked((long) 0x2C31334E19781208L) /* 851 */, + unchecked((long) 0x4FA98D7CEAEE6291L) /* 852 */, unchecked((long) 0x55C3862F665DB309L) /* 853 */, + unchecked((long) 0xBD0610175D53B1F3L) /* 854 */, unchecked((long) 0x46FE6CB840413F27L) /* 855 */, + unchecked((long) 0x3FE03792DF0CFA59L) /* 856 */, unchecked((long) 0xCFE700372EB85E8FL) /* 857 */, + unchecked((long) 0xA7BE29E7ADBCE118L) /* 858 */, unchecked((long) 0xE544EE5CDE8431DDL) /* 859 */, + unchecked((long) 0x8A781B1B41F1873EL) /* 860 */, unchecked((long) 0xA5C94C78A0D2F0E7L) /* 861 */, + unchecked((long) 0x39412E2877B60728L) /* 862 */, unchecked((long) 0xA1265EF3AFC9A62CL) /* 863 */, + unchecked((long) 0xBCC2770C6A2506C5L) /* 864 */, unchecked((long) 0x3AB66DD5DCE1CE12L) /* 865 */, + unchecked((long) 0xE65499D04A675B37L) /* 866 */, unchecked((long) 0x7D8F523481BFD216L) /* 867 */, + unchecked((long) 0x0F6F64FCEC15F389L) /* 868 */, unchecked((long) 0x74EFBE618B5B13C8L) /* 869 */, + unchecked((long) 0xACDC82B714273E1DL) /* 870 */, unchecked((long) 0xDD40BFE003199D17L) /* 871 */, + unchecked((long) 0x37E99257E7E061F8L) /* 872 */, unchecked((long) 0xFA52626904775AAAL) /* 873 */, + unchecked((long) 0x8BBBF63A463D56F9L) /* 874 */, unchecked((long) 0xF0013F1543A26E64L) /* 875 */, + unchecked((long) 0xA8307E9F879EC898L) /* 876 */, unchecked((long) 0xCC4C27A4150177CCL) /* 877 */, + unchecked((long) 0x1B432F2CCA1D3348L) /* 878 */, unchecked((long) 0xDE1D1F8F9F6FA013L) /* 879 */, + unchecked((long) 0x606602A047A7DDD6L) /* 880 */, unchecked((long) 0xD237AB64CC1CB2C7L) /* 881 */, + unchecked((long) 0x9B938E7225FCD1D3L) /* 882 */, unchecked((long) 0xEC4E03708E0FF476L) /* 883 */, + unchecked((long) 0xFEB2FBDA3D03C12DL) /* 884 */, unchecked((long) 0xAE0BCED2EE43889AL) /* 885 */, + unchecked((long) 0x22CB8923EBFB4F43L) /* 886 */, unchecked((long) 0x69360D013CF7396DL) /* 887 */, + unchecked((long) 0x855E3602D2D4E022L) /* 888 */, unchecked((long) 0x073805BAD01F784CL) /* 889 */, + unchecked((long) 0x33E17A133852F546L) /* 890 */, unchecked((long) 0xDF4874058AC7B638L) /* 891 */, + unchecked((long) 0xBA92B29C678AA14AL) /* 892 */, unchecked((long) 0x0CE89FC76CFAADCDL) /* 893 */, + unchecked((long) 0x5F9D4E0908339E34L) /* 894 */, unchecked((long) 0xF1AFE9291F5923B9L) /* 895 */, + unchecked((long) 0x6E3480F60F4A265FL) /* 896 */, unchecked((long) 0xEEBF3A2AB29B841CL) /* 897 */, + unchecked((long) 0xE21938A88F91B4ADL) /* 898 */, unchecked((long) 0x57DFEFF845C6D3C3L) /* 899 */, + unchecked((long) 0x2F006B0BF62CAAF2L) /* 900 */, unchecked((long) 0x62F479EF6F75EE78L) /* 901 */, + unchecked((long) 0x11A55AD41C8916A9L) /* 902 */, unchecked((long) 0xF229D29084FED453L) /* 903 */, + unchecked((long) 0x42F1C27B16B000E6L) /* 904 */, unchecked((long) 0x2B1F76749823C074L) /* 905 */, + unchecked((long) 0x4B76ECA3C2745360L) /* 906 */, unchecked((long) 0x8C98F463B91691BDL) /* 907 */, + unchecked((long) 0x14BCC93CF1ADE66AL) /* 908 */, unchecked((long) 0x8885213E6D458397L) /* 909 */, + unchecked((long) 0x8E177DF0274D4711L) /* 910 */, unchecked((long) 0xB49B73B5503F2951L) /* 911 */, + unchecked((long) 0x10168168C3F96B6BL) /* 912 */, unchecked((long) 0x0E3D963B63CAB0AEL) /* 913 */, + unchecked((long) 0x8DFC4B5655A1DB14L) /* 914 */, unchecked((long) 0xF789F1356E14DE5CL) /* 915 */, + unchecked((long) 0x683E68AF4E51DAC1L) /* 916 */, unchecked((long) 0xC9A84F9D8D4B0FD9L) /* 917 */, + unchecked((long) 0x3691E03F52A0F9D1L) /* 918 */, unchecked((long) 0x5ED86E46E1878E80L) /* 919 */, + unchecked((long) 0x3C711A0E99D07150L) /* 920 */, unchecked((long) 0x5A0865B20C4E9310L) /* 921 */, + unchecked((long) 0x56FBFC1FE4F0682EL) /* 922 */, unchecked((long) 0xEA8D5DE3105EDF9BL) /* 923 */, + unchecked((long) 0x71ABFDB12379187AL) /* 924 */, unchecked((long) 0x2EB99DE1BEE77B9CL) /* 925 */, + unchecked((long) 0x21ECC0EA33CF4523L) /* 926 */, unchecked((long) 0x59A4D7521805C7A1L) /* 927 */, + unchecked((long) 0x3896F5EB56AE7C72L) /* 928 */, unchecked((long) 0xAA638F3DB18F75DCL) /* 929 */, + unchecked((long) 0x9F39358DABE9808EL) /* 930 */, unchecked((long) 0xB7DEFA91C00B72ACL) /* 931 */, + unchecked((long) 0x6B5541FD62492D92L) /* 932 */, unchecked((long) 0x6DC6DEE8F92E4D5BL) /* 933 */, + unchecked((long) 0x353F57ABC4BEEA7EL) /* 934 */, unchecked((long) 0x735769D6DA5690CEL) /* 935 */, + unchecked((long) 0x0A234AA642391484L) /* 936 */, unchecked((long) 0xF6F9508028F80D9DL) /* 937 */, + unchecked((long) 0xB8E319A27AB3F215L) /* 938 */, unchecked((long) 0x31AD9C1151341A4DL) /* 939 */, + unchecked((long) 0x773C22A57BEF5805L) /* 940 */, unchecked((long) 0x45C7561A07968633L) /* 941 */, + unchecked((long) 0xF913DA9E249DBE36L) /* 942 */, unchecked((long) 0xDA652D9B78A64C68L) /* 943 */, + unchecked((long) 0x4C27A97F3BC334EFL) /* 944 */, unchecked((long) 0x76621220E66B17F4L) /* 945 */, + unchecked((long) 0x967743899ACD7D0BL) /* 946 */, unchecked((long) 0xF3EE5BCAE0ED6782L) /* 947 */, + unchecked((long) 0x409F753600C879FCL) /* 948 */, unchecked((long) 0x06D09A39B5926DB6L) /* 949 */, + unchecked((long) 0x6F83AEB0317AC588L) /* 950 */, unchecked((long) 0x01E6CA4A86381F21L) /* 951 */, + unchecked((long) 0x66FF3462D19F3025L) /* 952 */, unchecked((long) 0x72207C24DDFD3BFBL) /* 953 */, + unchecked((long) 0x4AF6B6D3E2ECE2EBL) /* 954 */, unchecked((long) 0x9C994DBEC7EA08DEL) /* 955 */, + unchecked((long) 0x49ACE597B09A8BC4L) /* 956 */, unchecked((long) 0xB38C4766CF0797BAL) /* 957 */, + unchecked((long) 0x131B9373C57C2A75L) /* 958 */, unchecked((long) 0xB1822CCE61931E58L) /* 959 */, + unchecked((long) 0x9D7555B909BA1C0CL) /* 960 */, unchecked((long) 0x127FAFDD937D11D2L) /* 961 */, + unchecked((long) 0x29DA3BADC66D92E4L) /* 962 */, unchecked((long) 0xA2C1D57154C2ECBCL) /* 963 */, + unchecked((long) 0x58C5134D82F6FE24L) /* 964 */, unchecked((long) 0x1C3AE3515B62274FL) /* 965 */, + unchecked((long) 0xE907C82E01CB8126L) /* 966 */, unchecked((long) 0xF8ED091913E37FCBL) /* 967 */, + unchecked((long) 0x3249D8F9C80046C9L) /* 968 */, unchecked((long) 0x80CF9BEDE388FB63L) /* 969 */, + unchecked((long) 0x1881539A116CF19EL) /* 970 */, unchecked((long) 0x5103F3F76BD52457L) /* 971 */, + unchecked((long) 0x15B7E6F5AE47F7A8L) /* 972 */, unchecked((long) 0xDBD7C6DED47E9CCFL) /* 973 */, + unchecked((long) 0x44E55C410228BB1AL) /* 974 */, unchecked((long) 0xB647D4255EDB4E99L) /* 975 */, + unchecked((long) 0x5D11882BB8AAFC30L) /* 976 */, unchecked((long) 0xF5098BBB29D3212AL) /* 977 */, + unchecked((long) 0x8FB5EA14E90296B3L) /* 978 */, unchecked((long) 0x677B942157DD025AL) /* 979 */, + unchecked((long) 0xFB58E7C0A390ACB5L) /* 980 */, unchecked((long) 0x89D3674C83BD4A01L) /* 981 */, + unchecked((long) 0x9E2DA4DF4BF3B93BL) /* 982 */, unchecked((long) 0xFCC41E328CAB4829L) /* 983 */, + unchecked((long) 0x03F38C96BA582C52L) /* 984 */, unchecked((long) 0xCAD1BDBD7FD85DB2L) /* 985 */, + unchecked((long) 0xBBB442C16082AE83L) /* 986 */, unchecked((long) 0xB95FE86BA5DA9AB0L) /* 987 */, + unchecked((long) 0xB22E04673771A93FL) /* 988 */, unchecked((long) 0x845358C9493152D8L) /* 989 */, + unchecked((long) 0xBE2A488697B4541EL) /* 990 */, unchecked((long) 0x95A2DC2DD38E6966L) /* 991 */, + unchecked((long) 0xC02C11AC923C852BL) /* 992 */, unchecked((long) 0x2388B1990DF2A87BL) /* 993 */, + unchecked((long) 0x7C8008FA1B4F37BEL) /* 994 */, unchecked((long) 0x1F70D0C84D54E503L) /* 995 */, + unchecked((long) 0x5490ADEC7ECE57D4L) /* 996 */, unchecked((long) 0x002B3C27D9063A3AL) /* 997 */, + unchecked((long) 0x7EAEA3848030A2BFL) /* 998 */, unchecked((long) 0xC602326DED2003C0L) /* 999 */, + unchecked((long) 0x83A7287D69A94086L) /* 1000 */, unchecked((long) 0xC57A5FCB30F57A8AL) /* 1001 */, + unchecked((long) 0xB56844E479EBE779L) /* 1002 */, unchecked((long) 0xA373B40F05DCBCE9L) /* 1003 */, + unchecked((long) 0xD71A786E88570EE2L) /* 1004 */, unchecked((long) 0x879CBACDBDE8F6A0L) /* 1005 */, + unchecked((long) 0x976AD1BCC164A32FL) /* 1006 */, unchecked((long) 0xAB21E25E9666D78BL) /* 1007 */, + unchecked((long) 0x901063AAE5E5C33CL) /* 1008 */, unchecked((long) 0x9818B34448698D90L) /* 1009 */, + unchecked((long) 0xE36487AE3E1E8ABBL) /* 1010 */, unchecked((long) 0xAFBDF931893BDCB4L) /* 1011 */, + unchecked((long) 0x6345A0DC5FBBD519L) /* 1012 */, unchecked((long) 0x8628FE269B9465CAL) /* 1013 */, + unchecked((long) 0x1E5D01603F9C51ECL) /* 1014 */, unchecked((long) 0x4DE44006A15049B7L) /* 1015 */, + unchecked((long) 0xBF6C70E5F776CBB1L) /* 1016 */, unchecked((long) 0x411218F2EF552BEDL) /* 1017 */, + unchecked((long) 0xCB0C0708705A36A3L) /* 1018 */, unchecked((long) 0xE74D14754F986044L) /* 1019 */, + unchecked((long) 0xCD56D9430EA8280EL) /* 1020 */, unchecked((long) 0xC12591D7535F5065L) /* 1021 */, + unchecked((long) 0xC83223F1720AEF96L) /* 1022 */, unchecked((long) 0xC3A0396F7363A51FL) /* 1023 */ + }; + + private const int DigestLength = 24; + + // + // registers + // + private long a, b, c; + private long byteCount; + + // + // buffers + // + private byte[] Buffer = new byte[8]; + private int bOff; + + private long[] x = new long[8]; + private int xOff; + + /** + * Standard constructor + */ + public TigerDigest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public TigerDigest(TigerDigest t) + { + a = t.a; + b = t.b; + c = t.c; + + Array.Copy(t.x, 0, x, 0, t.x.Length); + xOff = t.xOff; + + Array.Copy(t.Buffer, 0, Buffer, 0, t.Buffer.Length); + bOff = t.bOff; + + byteCount = t.byteCount; + } + + public string AlgorithmName + { + get { return "Tiger"; } + } + + public int GetDigestSize() + { + return DigestLength; + } + + public int GetByteLength() + { + return MyByteLength; + } + + private void ProcessWord( + byte[] b, + int off) + { + x[xOff++] = ((long)(b[off + 7] & 0xff) << 56) + | ((long)(b[off + 6] & 0xff) << 48) + | ((long)(b[off + 5] & 0xff) << 40) + | ((long)(b[off + 4] & 0xff) << 32) + | ((long)(b[off + 3] & 0xff) << 24) + | ((long)(b[off + 2] & 0xff) << 16) + | ((long)(b[off + 1] & 0xff) << 8) + | ((uint)(b[off + 0] & 0xff)); + + if (xOff == x.Length) + { + ProcessBlock(); + } + + bOff = 0; + } + + public void Update( + byte input) + { + Buffer[bOff++] = input; + + if (bOff == Buffer.Length) + { + ProcessWord(Buffer, 0); + } + + byteCount++; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + // + // fill the current word + // + while ((bOff != 0) && (length > 0)) + { + Update(input[inOff]); + + inOff++; + length--; + } + + // + // process whole words. + // + while (length > 8) + { + ProcessWord(input, inOff); + + inOff += 8; + length -= 8; + byteCount += 8; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } + + private void RoundABC( + long x, + long mul) + { + c ^= x ; + a -= t1[(int)c & 0xff] ^ t2[(int)(c >> 16) & 0xff] + ^ t3[(int)(c >> 32) & 0xff] ^ t4[(int)(c >> 48) & 0xff]; + b += t4[(int)(c >> 8) & 0xff] ^ t3[(int)(c >> 24) & 0xff] + ^ t2[(int)(c >> 40) & 0xff] ^ t1[(int)(c >> 56) & 0xff]; + b *= mul; + } + + private void RoundBCA( + long x, + long mul) + { + a ^= x ; + b -= t1[(int)a & 0xff] ^ t2[(int)(a >> 16) & 0xff] + ^ t3[(int)(a >> 32) & 0xff] ^ t4[(int)(a >> 48) & 0xff]; + c += t4[(int)(a >> 8) & 0xff] ^ t3[(int)(a >> 24) & 0xff] + ^ t2[(int)(a >> 40) & 0xff] ^ t1[(int)(a >> 56) & 0xff]; + c *= mul; + } + + private void RoundCAB( + long x, + long mul) + { + b ^= x ; + c -= t1[(int)b & 0xff] ^ t2[(int)(b >> 16) & 0xff] + ^ t3[(int)(b >> 32) & 0xff] ^ t4[(int)(b >> 48) & 0xff]; + a += t4[(int)(b >> 8) & 0xff] ^ t3[(int)(b >> 24) & 0xff] + ^ t2[(int)(b >> 40) & 0xff] ^ t1[(int)(b >> 56) & 0xff]; + a *= mul; + } + + private void KeySchedule() + { + x[0] -= x[7] ^ unchecked ((long) 0xA5A5A5A5A5A5A5A5L); + x[1] ^= x[0]; + x[2] += x[1]; + x[3] -= x[2] ^ ((~x[1]) << 19); + x[4] ^= x[3]; + x[5] += x[4]; + x[6] -= x[5] ^ (long) ((ulong) (~x[4]) >> 23); + x[7] ^= x[6]; + x[0] += x[7]; + x[1] -= x[0] ^ ((~x[7]) << 19); + x[2] ^= x[1]; + x[3] += x[2]; + x[4] -= x[3] ^ (long) ((ulong) (~x[2]) >> 23); + x[5] ^= x[4]; + x[6] += x[5]; + x[7] -= x[6] ^ 0x0123456789ABCDEFL; + } + + private void ProcessBlock() + { + // + // save abc + // + long aa = a; + long bb = b; + long cc = c; + + // + // rounds and schedule + // + RoundABC(x[0], 5); + RoundBCA(x[1], 5); + RoundCAB(x[2], 5); + RoundABC(x[3], 5); + RoundBCA(x[4], 5); + RoundCAB(x[5], 5); + RoundABC(x[6], 5); + RoundBCA(x[7], 5); + + KeySchedule(); + + RoundCAB(x[0], 7); + RoundABC(x[1], 7); + RoundBCA(x[2], 7); + RoundCAB(x[3], 7); + RoundABC(x[4], 7); + RoundBCA(x[5], 7); + RoundCAB(x[6], 7); + RoundABC(x[7], 7); + + KeySchedule(); + + RoundBCA(x[0], 9); + RoundCAB(x[1], 9); + RoundABC(x[2], 9); + RoundBCA(x[3], 9); + RoundCAB(x[4], 9); + RoundABC(x[5], 9); + RoundBCA(x[6], 9); + RoundCAB(x[7], 9); + + // + // feed forward + // + a ^= aa; + b -= bb; + c += cc; + + // + // clear the x buffer + // + xOff = 0; + for (int i = 0; i != x.Length; i++) + { + x[i] = 0; + } + } + + private void UnpackWord( + long r, + byte[] output, + int outOff) + { + output[outOff + 7] = (byte)(r >> 56); + output[outOff + 6] = (byte)(r >> 48); + output[outOff + 5] = (byte)(r >> 40); + output[outOff + 4] = (byte)(r >> 32); + output[outOff + 3] = (byte)(r >> 24); + output[outOff + 2] = (byte)(r >> 16); + output[outOff + 1] = (byte)(r >> 8); + output[outOff] = (byte)r; + } + + private void ProcessLength( + long bitLength) + { + x[7] = bitLength; + } + + private void Finish() + { + long bitLength = (byteCount << 3); + + Update((byte)0x01); + + while (bOff != 0) + { + Update((byte)0); + } + + ProcessLength(bitLength); + + ProcessBlock(); + } + + public int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(a, output, outOff); + UnpackWord(b, output, outOff + 8); + UnpackWord(c, output, outOff + 16); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public void Reset() + { + a = unchecked((long) 0x0123456789ABCDEFL); + b = unchecked((long) 0xFEDCBA9876543210L); + c = unchecked((long) 0xF096A5B4C3B2E187L); + + xOff = 0; + for (int i = 0; i != x.Length; i++) + { + x[i] = 0; + } + + bOff = 0; + for (int i = 0; i != Buffer.Length; i++) + { + Buffer[i] = 0; + } + + byteCount = 0; + } + } +} diff --git a/src/core/srcbc/crypto/digests/WhirlpoolDigest.cs b/src/core/srcbc/crypto/digests/WhirlpoolDigest.cs new file mode 100644 index 0000000..5ac0ff6 --- /dev/null +++ b/src/core/srcbc/crypto/digests/WhirlpoolDigest.cs @@ -0,0 +1,397 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Implementation of WhirlpoolDigest, based on Java source published by Barreto + * and Rijmen. + * + */ + public sealed class WhirlpoolDigest : IDigest + { + private const int BYTE_LENGTH = 64; + + private const int DIGEST_LENGTH_BYTES = 512 / 8; + private const int ROUNDS = 10; + private const int REDUCTION_POLYNOMIAL = 0x011d; // 2^8 + 2^4 + 2^3 + 2 + 1; + + private static readonly int[] SBOX = + { + 0x18, 0x23, 0xc6, 0xe8, 0x87, 0xb8, 0x01, 0x4f, 0x36, 0xa6, 0xd2, 0xf5, 0x79, 0x6f, 0x91, 0x52, + 0x60, 0xbc, 0x9b, 0x8e, 0xa3, 0x0c, 0x7b, 0x35, 0x1d, 0xe0, 0xd7, 0xc2, 0x2e, 0x4b, 0xfe, 0x57, + 0x15, 0x77, 0x37, 0xe5, 0x9f, 0xf0, 0x4a, 0xda, 0x58, 0xc9, 0x29, 0x0a, 0xb1, 0xa0, 0x6b, 0x85, + 0xbd, 0x5d, 0x10, 0xf4, 0xcb, 0x3e, 0x05, 0x67, 0xe4, 0x27, 0x41, 0x8b, 0xa7, 0x7d, 0x95, 0xd8, + 0xfb, 0xee, 0x7c, 0x66, 0xdd, 0x17, 0x47, 0x9e, 0xca, 0x2d, 0xbf, 0x07, 0xad, 0x5a, 0x83, 0x33, + 0x63, 0x02, 0xaa, 0x71, 0xc8, 0x19, 0x49, 0xd9, 0xf2, 0xe3, 0x5b, 0x88, 0x9a, 0x26, 0x32, 0xb0, + 0xe9, 0x0f, 0xd5, 0x80, 0xbe, 0xcd, 0x34, 0x48, 0xff, 0x7a, 0x90, 0x5f, 0x20, 0x68, 0x1a, 0xae, + 0xb4, 0x54, 0x93, 0x22, 0x64, 0xf1, 0x73, 0x12, 0x40, 0x08, 0xc3, 0xec, 0xdb, 0xa1, 0x8d, 0x3d, + 0x97, 0x00, 0xcf, 0x2b, 0x76, 0x82, 0xd6, 0x1b, 0xb5, 0xaf, 0x6a, 0x50, 0x45, 0xf3, 0x30, 0xef, + 0x3f, 0x55, 0xa2, 0xea, 0x65, 0xba, 0x2f, 0xc0, 0xde, 0x1c, 0xfd, 0x4d, 0x92, 0x75, 0x06, 0x8a, + 0xb2, 0xe6, 0x0e, 0x1f, 0x62, 0xd4, 0xa8, 0x96, 0xf9, 0xc5, 0x25, 0x59, 0x84, 0x72, 0x39, 0x4c, + 0x5e, 0x78, 0x38, 0x8c, 0xd1, 0xa5, 0xe2, 0x61, 0xb3, 0x21, 0x9c, 0x1e, 0x43, 0xc7, 0xfc, 0x04, + 0x51, 0x99, 0x6d, 0x0d, 0xfa, 0xdf, 0x7e, 0x24, 0x3b, 0xab, 0xce, 0x11, 0x8f, 0x4e, 0xb7, 0xeb, + 0x3c, 0x81, 0x94, 0xf7, 0xb9, 0x13, 0x2c, 0xd3, 0xe7, 0x6e, 0xc4, 0x03, 0x56, 0x44, 0x7f, 0xa9, + 0x2a, 0xbb, 0xc1, 0x53, 0xdc, 0x0b, 0x9d, 0x6c, 0x31, 0x74, 0xf6, 0x46, 0xac, 0x89, 0x14, 0xe1, + 0x16, 0x3a, 0x69, 0x09, 0x70, 0xb6, 0xd0, 0xed, 0xcc, 0x42, 0x98, 0xa4, 0x28, 0x5c, 0xf8, 0x86 + }; + + private static readonly long[] C0 = new long[256]; + private static readonly long[] C1 = new long[256]; + private static readonly long[] C2 = new long[256]; + private static readonly long[] C3 = new long[256]; + private static readonly long[] C4 = new long[256]; + private static readonly long[] C5 = new long[256]; + private static readonly long[] C6 = new long[256]; + private static readonly long[] C7 = new long[256]; + + private readonly long[] _rc = new long[ROUNDS + 1]; + + /* + * increment() can be implemented in this way using 2 arrays or + * by having some temporary variables that are used to set the + * value provided by EIGHT[i] and carry within the loop. + * + * not having done any timing, this seems likely to be faster + * at the slight expense of 32*(sizeof short) bytes + */ + private static readonly short[] EIGHT = new short[BITCOUNT_ARRAY_SIZE]; + + static WhirlpoolDigest() + { + EIGHT[BITCOUNT_ARRAY_SIZE - 1] = 8; + + for (int i = 0; i < 256; i++) + { + int v1 = SBOX[i]; + int v2 = maskWithReductionPolynomial(v1 << 1); + int v4 = maskWithReductionPolynomial(v2 << 1); + int v5 = v4 ^ v1; + int v8 = maskWithReductionPolynomial(v4 << 1); + int v9 = v8 ^ v1; + + C0[i] = packIntoLong(v1, v1, v4, v1, v8, v5, v2, v9); + C1[i] = packIntoLong(v9, v1, v1, v4, v1, v8, v5, v2); + C2[i] = packIntoLong(v2, v9, v1, v1, v4, v1, v8, v5); + C3[i] = packIntoLong(v5, v2, v9, v1, v1, v4, v1, v8); + C4[i] = packIntoLong(v8, v5, v2, v9, v1, v1, v4, v1); + C5[i] = packIntoLong(v1, v8, v5, v2, v9, v1, v1, v4); + C6[i] = packIntoLong(v4, v1, v8, v5, v2, v9, v1, v1); + C7[i] = packIntoLong(v1, v4, v1, v8, v5, v2, v9, v1); + } + } + + public WhirlpoolDigest() + { + _rc[0] = 0L; + for (int r = 1; r <= ROUNDS; r++) + { + int i = 8 * (r - 1); + _rc[r] = (long)((ulong)C0[i] & 0xff00000000000000L) ^ + (C1[i + 1] & (long) 0x00ff000000000000L) ^ + (C2[i + 2] & (long) 0x0000ff0000000000L) ^ + (C3[i + 3] & (long) 0x000000ff00000000L) ^ + (C4[i + 4] & (long) 0x00000000ff000000L) ^ + (C5[i + 5] & (long) 0x0000000000ff0000L) ^ + (C6[i + 6] & (long) 0x000000000000ff00L) ^ + (C7[i + 7] & (long) 0x00000000000000ffL); + } + } + + private static long packIntoLong(int b7, int b6, int b5, int b4, int b3, int b2, int b1, int b0) + { + return + ((long)b7 << 56) ^ + ((long)b6 << 48) ^ + ((long)b5 << 40) ^ + ((long)b4 << 32) ^ + ((long)b3 << 24) ^ + ((long)b2 << 16) ^ + ((long)b1 << 8) ^ + b0; + } + + /* + * int's are used to prevent sign extension. The values that are really being used are + * actually just 0..255 + */ + private static int maskWithReductionPolynomial(int input) + { + int rv = input; + if (rv >= 0x100L) // high bit set + { + rv ^= REDUCTION_POLYNOMIAL; // reduced by the polynomial + } + return rv; + } + + // --------------------------------------------------------------------------------------// + + // -- buffer information -- + private const int BITCOUNT_ARRAY_SIZE = 32; + private byte[] _buffer = new byte[64]; + private int _bufferPos; + private short[] _bitCount = new short[BITCOUNT_ARRAY_SIZE]; + + // -- internal hash state -- + private long[] _hash = new long[8]; + private long[] _K = new long[8]; // the round key + private long[] _L = new long[8]; + private long[] _block = new long[8]; // mu (buffer) + private long[] _state = new long[8]; // the current "cipher" state + + + + /** + * Copy constructor. This will copy the state of the provided message + * digest. + */ + public WhirlpoolDigest(WhirlpoolDigest originalDigest) + { + Array.Copy(originalDigest._rc, 0, _rc, 0, _rc.Length); + + Array.Copy(originalDigest._buffer, 0, _buffer, 0, _buffer.Length); + + this._bufferPos = originalDigest._bufferPos; + Array.Copy(originalDigest._bitCount, 0, _bitCount, 0, _bitCount.Length); + + // -- internal hash state -- + Array.Copy(originalDigest._hash, 0, _hash, 0, _hash.Length); + Array.Copy(originalDigest._K, 0, _K, 0, _K.Length); + Array.Copy(originalDigest._L, 0, _L, 0, _L.Length); + Array.Copy(originalDigest._block, 0, _block, 0, _block.Length); + Array.Copy(originalDigest._state, 0, _state, 0, _state.Length); + } + + public string AlgorithmName + { + get { return "Whirlpool"; } + } + + public int GetDigestSize() + { + return DIGEST_LENGTH_BYTES; + } + + public int DoFinal(byte[] output, int outOff) + { + // sets output[outOff] .. output[outOff+DIGEST_LENGTH_BYTES] + finish(); + + for (int i = 0; i < 8; i++) + { + convertLongToByteArray(_hash[i], output, outOff + (i * 8)); + } + + Reset(); + + return GetDigestSize(); + } + + /** + * Reset the chaining variables + */ + public void Reset() + { + // set variables to null, blank, whatever + _bufferPos = 0; + Array.Clear(_bitCount, 0, _bitCount.Length); + Array.Clear(_buffer, 0, _buffer.Length); + Array.Clear(_hash, 0, _hash.Length); + Array.Clear(_K, 0, _K.Length); + Array.Clear(_L, 0, _L.Length); + Array.Clear(_block, 0, _block.Length); + Array.Clear(_state, 0, _state.Length); + } + + // this takes a buffer of information and fills the block + private void processFilledBuffer() + { + // copies into the block... + for (int i = 0; i < _state.Length; i++) + { + _block[i] = bytesToLongFromBuffer(_buffer, i * 8); + } + processBlock(); + _bufferPos = 0; + Array.Clear(_buffer, 0, _buffer.Length); + } + + private static long bytesToLongFromBuffer(byte[] buffer, int startPos) + { + long rv = (((buffer[startPos + 0] & 0xffL) << 56) | + ((buffer[startPos + 1] & 0xffL) << 48) | + ((buffer[startPos + 2] & 0xffL) << 40) | + ((buffer[startPos + 3] & 0xffL) << 32) | + ((buffer[startPos + 4] & 0xffL) << 24) | + ((buffer[startPos + 5] & 0xffL) << 16) | + ((buffer[startPos + 6] & 0xffL) << 8) | + ((buffer[startPos + 7]) & 0xffL)); + + return rv; + } + + private static void convertLongToByteArray(long inputLong, byte[] outputArray, int offSet) + { + for (int i = 0; i < 8; i++) + { + outputArray[offSet + i] = (byte)((inputLong >> (56 - (i * 8))) & 0xff); + } + } + + private void processBlock() + { + // buffer contents have been transferred to the _block[] array via + // processFilledBuffer + + // compute and apply K^0 + for (int i = 0; i < 8; i++) + { + _state[i] = _block[i] ^ (_K[i] = _hash[i]); + } + + // iterate over the rounds + for (int round = 1; round <= ROUNDS; round++) + { + for (int i = 0; i < 8; i++) + { + _L[i] = 0; + _L[i] ^= C0[(int)(_K[(i - 0) & 7] >> 56) & 0xff]; + _L[i] ^= C1[(int)(_K[(i - 1) & 7] >> 48) & 0xff]; + _L[i] ^= C2[(int)(_K[(i - 2) & 7] >> 40) & 0xff]; + _L[i] ^= C3[(int)(_K[(i - 3) & 7] >> 32) & 0xff]; + _L[i] ^= C4[(int)(_K[(i - 4) & 7] >> 24) & 0xff]; + _L[i] ^= C5[(int)(_K[(i - 5) & 7] >> 16) & 0xff]; + _L[i] ^= C6[(int)(_K[(i - 6) & 7] >> 8) & 0xff]; + _L[i] ^= C7[(int)(_K[(i - 7) & 7]) & 0xff]; + } + + Array.Copy(_L, 0, _K, 0, _K.Length); + + _K[0] ^= _rc[round]; + + // apply the round transformation + for (int i = 0; i < 8; i++) + { + _L[i] = _K[i]; + + _L[i] ^= C0[(int)(_state[(i - 0) & 7] >> 56) & 0xff]; + _L[i] ^= C1[(int)(_state[(i - 1) & 7] >> 48) & 0xff]; + _L[i] ^= C2[(int)(_state[(i - 2) & 7] >> 40) & 0xff]; + _L[i] ^= C3[(int)(_state[(i - 3) & 7] >> 32) & 0xff]; + _L[i] ^= C4[(int)(_state[(i - 4) & 7] >> 24) & 0xff]; + _L[i] ^= C5[(int)(_state[(i - 5) & 7] >> 16) & 0xff]; + _L[i] ^= C6[(int)(_state[(i - 6) & 7] >> 8) & 0xff]; + _L[i] ^= C7[(int)(_state[(i - 7) & 7]) & 0xff]; + } + + // save the current state + Array.Copy(_L, 0, _state, 0, _state.Length); + } + + // apply Miuaguchi-Preneel compression + for (int i = 0; i < 8; i++) + { + _hash[i] ^= _state[i] ^ _block[i]; + } + + } + + public void Update(byte input) + { + _buffer[_bufferPos] = input; + + //Console.WriteLine("adding to buffer = "+_buffer[_bufferPos]); + + ++_bufferPos; + + if (_bufferPos == _buffer.Length) + { + processFilledBuffer(); + } + + increment(); + } + + private void increment() + { + int carry = 0; + for (int i = _bitCount.Length - 1; i >= 0; i--) + { + int sum = (_bitCount[i] & 0xff) + EIGHT[i] + carry; + + carry = sum >> 8; + _bitCount[i] = (short)(sum & 0xff); + } + } + + public void BlockUpdate(byte[] input, int inOff, int length) + { + while (length > 0) + { + Update(input[inOff]); + ++inOff; + --length; + } + + } + + private void finish() + { + /* + * this makes a copy of the current bit length. at the expense of an + * object creation of 32 bytes rather than providing a _stopCounting + * boolean which was the alternative I could think of. + */ + byte[] bitLength = copyBitLength(); + + _buffer[_bufferPos++] |= 0x80; + + if (_bufferPos == _buffer.Length) + { + processFilledBuffer(); + } + + /* + * Final block contains + * [ ... data .... ][0][0][0][ length ] + * + * if [ length ] cannot fit. Need to create a new block. + */ + if (_bufferPos > 32) + { + while (_bufferPos != 0) + { + Update((byte)0); + } + } + + while (_bufferPos <= 32) + { + Update((byte)0); + } + + // copy the length information to the final 32 bytes of the + // 64 byte block.... + Array.Copy(bitLength, 0, _buffer, 32, bitLength.Length); + + processFilledBuffer(); + } + + private byte[] copyBitLength() + { + byte[] rv = new byte[BITCOUNT_ARRAY_SIZE]; + for (int i = 0; i < rv.Length; i++) + { + rv[i] = (byte)(_bitCount[i] & 0xff); + } + return rv; + } + + public int GetByteLength() + { + return BYTE_LENGTH; + } + } +} diff --git a/src/core/srcbc/crypto/encodings/ISO9796d1Encoding.cs b/src/core/srcbc/crypto/encodings/ISO9796d1Encoding.cs new file mode 100644 index 0000000..02619d5 --- /dev/null +++ b/src/core/srcbc/crypto/encodings/ISO9796d1Encoding.cs @@ -0,0 +1,253 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Encodings +{ + /** + * ISO 9796-1 padding. Note in the light of recent results you should + * only use this with RSA (rather than the "simpler" Rabin keys) and you + * should never use it with anything other than a hash (ie. even if the + * message is small don't sign the message, sign it's hash) or some "random" + * value. See your favorite search engine for details. + */ + public class ISO9796d1Encoding + : IAsymmetricBlockCipher + { + private static readonly byte[] shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf, + 0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 }; + private static readonly byte[] inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc, + 0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 }; + + private readonly IAsymmetricBlockCipher engine; + private bool forEncryption; + private int bitSize; + private int padBits = 0; + + public ISO9796d1Encoding( + IAsymmetricBlockCipher cipher) + { + this.engine = cipher; + } + + public string AlgorithmName + { + get { return engine.AlgorithmName + "/ISO9796-1Padding"; } + } + + public IAsymmetricBlockCipher GetUnderlyingCipher() + { + return engine; + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + RsaKeyParameters kParam; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + kParam = (RsaKeyParameters)rParam.Parameters; + } + else + { + kParam = (RsaKeyParameters)parameters; + } + + engine.Init(forEncryption, parameters); + + bitSize = kParam.Modulus.BitLength; + + this.forEncryption = forEncryption; + } + + /** + * return the input block size. The largest message we can process + * is (key_size_in_bits + 3)/16, which in our world comes to + * key_size_in_bytes / 2. + */ + public int GetInputBlockSize() + { + int baseBlockSize = engine.GetInputBlockSize(); + + if (forEncryption) + { + return (baseBlockSize + 1) / 2; + } + else + { + return baseBlockSize; + } + } + + /** + * return the maximum possible size for the output. + */ + public int GetOutputBlockSize() + { + int baseBlockSize = engine.GetOutputBlockSize(); + + if (forEncryption) + { + return baseBlockSize; + } + else + { + return (baseBlockSize + 1) / 2; + } + } + + /** + * set the number of bits in the next message to be treated as + * pad bits. + */ + public void SetPadBits( + int padBits) + { + if (padBits > 7) + { + throw new ArgumentException("padBits > 7"); + } + + this.padBits = padBits; + } + + /** + * retrieve the number of pad bits in the last decoded message. + */ + public int GetPadBits() + { + return padBits; + } + + public byte[] ProcessBlock( + byte[] input, + int inOff, + int length) + { + if (forEncryption) + { + return EncodeBlock(input, inOff, length); + } + else + { + return DecodeBlock(input, inOff, length); + } + } + + private byte[] EncodeBlock( + byte[] input, + int inOff, + int inLen) + { + byte[] block = new byte[(bitSize + 7) / 8]; + int r = padBits + 1; + int z = inLen; + int t = (bitSize + 13) / 16; + + for (int i = 0; i < t; i += z) + { + if (i > t - z) + { + Array.Copy(input, inOff + inLen - (t - i), + block, block.Length - t, t - i); + } + else + { + Array.Copy(input, inOff, block, block.Length - (i + z), z); + } + } + + for (int i = block.Length - 2 * t; i != block.Length; i += 2) + { + byte val = block[block.Length - t + i / 2]; + + block[i] = (byte)((shadows[(uint) (val & 0xff) >> 4] << 4) + | shadows[val & 0x0f]); + block[i + 1] = val; + } + + block[block.Length - 2 * z] ^= (byte) r; + block[block.Length - 1] = (byte)((block[block.Length - 1] << 4) | 0x06); + + int maxBit = (8 - (bitSize - 1) % 8); + int offSet = 0; + + if (maxBit != 8) + { + block[0] &= (byte) ((ushort) 0xff >> maxBit); + block[0] |= (byte) ((ushort) 0x80 >> maxBit); + } + else + { + block[0] = 0x00; + block[1] |= 0x80; + offSet = 1; + } + + return engine.ProcessBlock(block, offSet, block.Length - offSet); + } + + /** + * @exception InvalidCipherTextException if the decrypted block is not a valid ISO 9796 bit string + */ + private byte[] DecodeBlock( + byte[] input, + int inOff, + int inLen) + { + byte[] block = engine.ProcessBlock(input, inOff, inLen); + int r = 1; + int t = (bitSize + 13) / 16; + + if ((block[block.Length - 1] & 0x0f) != 0x6) + { + throw new InvalidCipherTextException("invalid forcing byte in block"); + } + + block[block.Length - 1] = + (byte)(((ushort)(block[block.Length - 1] & 0xff) >> 4) + | ((inverse[(block[block.Length - 2] & 0xff) >> 4]) << 4)); + + block[0] = (byte)((shadows[(uint) (block[1] & 0xff) >> 4] << 4) + | shadows[block[1] & 0x0f]); + + bool boundaryFound = false; + int boundary = 0; + + for (int i = block.Length - 1; i >= block.Length - 2 * t; i -= 2) + { + int val = ((shadows[(uint) (block[i] & 0xff) >> 4] << 4) + | shadows[block[i] & 0x0f]); + + if (((block[i - 1] ^ val) & 0xff) != 0) + { + if (!boundaryFound) + { + boundaryFound = true; + r = (block[i - 1] ^ val) & 0xff; + boundary = i - 1; + } + else + { + throw new InvalidCipherTextException("invalid tsums in block"); + } + } + } + + block[boundary] = 0; + + byte[] nblock = new byte[(block.Length - boundary) / 2]; + + for (int i = 0; i < nblock.Length; i++) + { + nblock[i] = block[2 * i + boundary + 1]; + } + + padBits = r - 1; + + return nblock; + } + } +} diff --git a/src/core/srcbc/crypto/encodings/OaepEncoding.cs b/src/core/srcbc/crypto/encodings/OaepEncoding.cs new file mode 100644 index 0000000..1d00171 --- /dev/null +++ b/src/core/srcbc/crypto/encodings/OaepEncoding.cs @@ -0,0 +1,334 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Encodings +{ + /** + * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2. + */ + public class OaepEncoding + : IAsymmetricBlockCipher + { + private byte[] defHash; + private IDigest hash; + + private IAsymmetricBlockCipher engine; + private SecureRandom random; + private bool forEncryption; + + public OaepEncoding( + IAsymmetricBlockCipher cipher) + : this(cipher, new Sha1Digest(), null) + { + } + + public OaepEncoding( + IAsymmetricBlockCipher cipher, + IDigest hash) + : this(cipher, hash, null) + { + } + + public OaepEncoding( + IAsymmetricBlockCipher cipher, + IDigest hash, + byte[] encodingParams) + { + this.engine = cipher; + this.hash = hash; + this.defHash = new byte[hash.GetDigestSize()]; + + if (encodingParams != null) + { + hash.BlockUpdate(encodingParams, 0, encodingParams.Length); + } + + hash.DoFinal(defHash, 0); + } + + public IAsymmetricBlockCipher GetUnderlyingCipher() + { + return engine; + } + + public string AlgorithmName + { + get { return engine.AlgorithmName + "/OAEPPadding"; } + } + + public void Init( + bool forEncryption, + ICipherParameters param) + { + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + this.random = rParam.Random; + } + else + { + this.random = new SecureRandom(); + } + + engine.Init(forEncryption, param); + + this.forEncryption = forEncryption; + } + + public int GetInputBlockSize() + { + int baseBlockSize = engine.GetInputBlockSize(); + + if (forEncryption) + { + return baseBlockSize - 1 - 2 * defHash.Length; + } + else + { + return baseBlockSize; + } + } + + public int GetOutputBlockSize() + { + int baseBlockSize = engine.GetOutputBlockSize(); + + if (forEncryption) + { + return baseBlockSize; + } + else + { + return baseBlockSize - 1 - 2 * defHash.Length; + } + } + + public byte[] ProcessBlock( + byte[] inBytes, + int inOff, + int inLen) + { + if (forEncryption) + { + return encodeBlock(inBytes, inOff, inLen); + } + else + { + return decodeBlock(inBytes, inOff, inLen); + } + } + + private byte[] encodeBlock( + byte[] inBytes, + int inOff, + int inLen) + { + byte[] block = new byte[GetInputBlockSize() + 1 + 2 * defHash.Length]; + + // + // copy in the message + // + Array.Copy(inBytes, inOff, block, block.Length - inLen, inLen); + + // + // add sentinel + // + block[block.Length - inLen - 1] = 0x01; + + // + // as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0) + // + + // + // add the hash of the encoding params. + // + Array.Copy(defHash, 0, block, defHash.Length, defHash.Length); + + // + // generate the seed. + // + byte[] seed = random.GenerateSeed(defHash.Length); + + // + // mask the message block. + // + byte[] mask = maskGeneratorFunction1(seed, 0, seed.Length, block.Length - defHash.Length); + + for (int i = defHash.Length; i != block.Length; i++) + { + block[i] ^= mask[i - defHash.Length]; + } + + // + // add in the seed + // + Array.Copy(seed, 0, block, 0, defHash.Length); + + // + // mask the seed. + // + mask = maskGeneratorFunction1( + block, defHash.Length, block.Length - defHash.Length, defHash.Length); + + for (int i = 0; i != defHash.Length; i++) + { + block[i] ^= mask[i]; + } + + return engine.ProcessBlock(block, 0, block.Length); + } + + /** + * @exception InvalidCipherTextException if the decrypted block turns out to + * be badly formatted. + */ + private byte[] decodeBlock( + byte[] inBytes, + int inOff, + int inLen) + { + byte[] data = engine.ProcessBlock(inBytes, inOff, inLen); + byte[] block = null; + + // + // as we may have zeros in our leading bytes for the block we produced + // on encryption, we need to make sure our decrypted block comes back + // the same size. + // + if (data.Length < engine.GetOutputBlockSize()) + { + block = new byte[engine.GetOutputBlockSize()]; + + Array.Copy(data, 0, block, block.Length - data.Length, data.Length); + } + else + { + block = data; + } + + if (block.Length < (2 * defHash.Length) + 1) + { + throw new InvalidCipherTextException("data too short"); + } + + // + // unmask the seed. + // + byte[] mask = maskGeneratorFunction1( + block, defHash.Length, block.Length - defHash.Length, defHash.Length); + + for (int i = 0; i != defHash.Length; i++) + { + block[i] ^= mask[i]; + } + + // + // unmask the message block. + // + mask = maskGeneratorFunction1(block, 0, defHash.Length, block.Length - defHash.Length); + + for (int i = defHash.Length; i != block.Length; i++) + { + block[i] ^= mask[i - defHash.Length]; + } + + // + // check the hash of the encoding params. + // + for (int i = 0; i != defHash.Length; i++) + { + if (defHash[i] != block[defHash.Length + i]) + { + throw new InvalidCipherTextException("data hash wrong"); + } + } + + // + // find the data block + // + int start; + for (start = 2 * defHash.Length; start != block.Length; start++) + { + if (block[start] == 1 || block[start] != 0) + { + break; + } + } + + if (start >= (block.Length - 1) || block[start] != 1) + { + throw new InvalidCipherTextException("data start wrong " + start); + } + + start++; + + // + // extract the data block + // + byte[] output = new byte[block.Length - start]; + + Array.Copy(block, start, output, 0, output.Length); + + return output; + } + + /** + * int to octet string. + */ + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint)i >> 24); + sp[1] = (byte)((uint)i >> 16); + sp[2] = (byte)((uint)i >> 8); + sp[3] = (byte)((uint)i >> 0); + } + + /** + * mask generator function, as described in PKCS1v2. + */ + private byte[] maskGeneratorFunction1( + byte[] Z, + int zOff, + int zLen, + int length) + { + byte[] mask = new byte[length]; + byte[] hashBuf = new byte[defHash.Length]; + byte[] C = new byte[4]; + int counter = 0; + + hash.Reset(); + + do + { + ItoOSP(counter, C); + + hash.BlockUpdate(Z, zOff, zLen); + hash.BlockUpdate(C, 0, C.Length); + hash.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * defHash.Length, defHash.Length); + } + while (++counter < (length / defHash.Length)); + + if ((counter * defHash.Length) < length) + { + ItoOSP(counter, C); + + hash.BlockUpdate(Z, zOff, zLen); + hash.BlockUpdate(C, 0, C.Length); + hash.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * defHash.Length, mask.Length - (counter * defHash.Length)); + } + + return mask; + } + } +} + diff --git a/src/core/srcbc/crypto/encodings/Pkcs1Encoding.cs b/src/core/srcbc/crypto/encodings/Pkcs1Encoding.cs new file mode 100644 index 0000000..b813728 --- /dev/null +++ b/src/core/srcbc/crypto/encodings/Pkcs1Encoding.cs @@ -0,0 +1,229 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Encodings +{ + /** + * this does your basic Pkcs 1 v1.5 padding - whether or not you should be using this + * depends on your application - see Pkcs1 Version 2 for details. + */ + public class Pkcs1Encoding + : IAsymmetricBlockCipher + { + /** + * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to + * work with one of these set the system property Org.BouncyCastle.Pkcs1.Strict to false. + */ + public const string StrictLengthEnabledProperty = "Org.BouncyCastle.Pkcs1.Strict"; + + private const int HeaderLength = 10; + + /** + * The same effect can be achieved by setting the static property directly + *

+ * The static property is checked during construction of the encoding object, it is set to + * true by default. + *

+ */ + public static bool StrictLengthEnabled + { + get { return strictLengthEnabled[0]; } + set { strictLengthEnabled[0] = value; } + } + + private static readonly bool[] strictLengthEnabled; + + static Pkcs1Encoding() + { + string strictProperty = Platform.GetEnvironmentVariable(StrictLengthEnabledProperty); + + strictLengthEnabled = new bool[]{ strictProperty == null || strictProperty.Equals("true")}; + } + + + private SecureRandom random; + private IAsymmetricBlockCipher engine; + private bool forEncryption; + private bool forPrivateKey; + private bool useStrictLength; + + /** + * Basic constructor. + * @param cipher + */ + public Pkcs1Encoding( + IAsymmetricBlockCipher cipher) + { + this.engine = cipher; + this.useStrictLength = StrictLengthEnabled; + } + + public IAsymmetricBlockCipher GetUnderlyingCipher() + { + return engine; + } + + public string AlgorithmName + { + get { return engine.AlgorithmName + "/PKCS1Padding"; } + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + AsymmetricKeyParameter kParam; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + kParam = (AsymmetricKeyParameter)rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + kParam = (AsymmetricKeyParameter)parameters; + } + + engine.Init(forEncryption, parameters); + + this.forPrivateKey = kParam.IsPrivate; + this.forEncryption = forEncryption; + } + + public int GetInputBlockSize() + { + int baseBlockSize = engine.GetInputBlockSize(); + + return forEncryption + ? baseBlockSize - HeaderLength + : baseBlockSize; + } + + public int GetOutputBlockSize() + { + int baseBlockSize = engine.GetOutputBlockSize(); + + return forEncryption + ? baseBlockSize + : baseBlockSize - HeaderLength; + } + + public byte[] ProcessBlock( + byte[] input, + int inOff, + int length) + { + return forEncryption + ? EncodeBlock(input, inOff, length) + : DecodeBlock(input, inOff, length); + } + + private byte[] EncodeBlock( + byte[] input, + int inOff, + int inLen) + { + byte[] block = new byte[engine.GetInputBlockSize()]; + + if (forPrivateKey) + { + block[0] = 0x01; // type code 1 + + for (int i = 1; i != block.Length - inLen - 1; i++) + { + block[i] = (byte)0xFF; + } + } + else + { + random.NextBytes(block); // random fill + + block[0] = 0x02; // type code 2 + + // + // a zero byte marks the end of the padding, so all + // the pad bytes must be non-zero. + // + for (int i = 1; i != block.Length - inLen - 1; i++) + { + while (block[i] == 0) + { + block[i] = (byte)random.NextInt(); + } + } + } + + block[block.Length - inLen - 1] = 0x00; // mark the end of the padding + Array.Copy(input, inOff, block, block.Length - inLen, inLen); + + return engine.ProcessBlock(block, 0, block.Length); + } + + /** + * @exception InvalidCipherTextException if the decrypted block is not in Pkcs1 format. + */ + private byte[] DecodeBlock( + byte[] input, + int inOff, + int inLen) + { + byte[] block = engine.ProcessBlock(input, inOff, inLen); + + if (block.Length < GetOutputBlockSize()) + { + throw new InvalidCipherTextException("block truncated"); + } + + byte type = block[0]; + + if (type != 1 && type != 2) + { + throw new InvalidCipherTextException("unknown block type"); + } + + if (useStrictLength && block.Length != engine.GetOutputBlockSize()) + { + throw new InvalidCipherTextException("block incorrect size"); + } + + // + // find and extract the message block. + // + int start; + for (start = 1; start != block.Length; start++) + { + byte pad = block[start]; + + if (pad == 0) + { + break; + } + + if (type == 1 && pad != (byte)0xff) + { + throw new InvalidCipherTextException("block padding incorrect"); + } + } + + start++; // data should start at the next byte + + if (start >= block.Length || start < HeaderLength) + { + throw new InvalidCipherTextException("no data in block"); + } + + byte[] result = new byte[block.Length - start]; + + Array.Copy(block, start, result, 0, result.Length); + + return result; + } + } + +} diff --git a/src/core/srcbc/crypto/engines/AesEngine.cs b/src/core/srcbc/crypto/engines/AesEngine.cs new file mode 100644 index 0000000..9b802b1 --- /dev/null +++ b/src/core/srcbc/crypto/engines/AesEngine.cs @@ -0,0 +1,550 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + + /** + * an implementation of the AES (Rijndael), from FIPS-197. + *

+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * There are three levels of tradeoff of speed vs memory + * Because java has no preprocessor, they are written as three separate classes from which to choose + * + * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption + * and 4 for decryption. + * + * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes, + * adding 12 rotate operations per round to compute the values contained in the other tables from + * the contents of the first. + * + * The slowest version uses no static tables at all and computes the values in each round. + *

+ *

+ * This file contains the middle performance version with 2Kbytes of static tables for round precomputation. + *

+ */ + public class AesEngine + : IBlockCipher + { + // The S box + private static readonly byte[] S = + { + 99, 124, 119, 123, 242, 107, 111, 197, + 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, + 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, + 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, + 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, + 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, + 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, + 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, + 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, + 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, + 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, + 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, + 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, + 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, + 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, + 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, + 65, 153, 45, 15, 176, 84, 187, 22, + }; + + // The inverse S-box + private static readonly byte[] Si = + { + 82, 9, 106, 213, 48, 54, 165, 56, + 191, 64, 163, 158, 129, 243, 215, 251, + 124, 227, 57, 130, 155, 47, 255, 135, + 52, 142, 67, 68, 196, 222, 233, 203, + 84, 123, 148, 50, 166, 194, 35, 61, + 238, 76, 149, 11, 66, 250, 195, 78, + 8, 46, 161, 102, 40, 217, 36, 178, + 118, 91, 162, 73, 109, 139, 209, 37, + 114, 248, 246, 100, 134, 104, 152, 22, + 212, 164, 92, 204, 93, 101, 182, 146, + 108, 112, 72, 80, 253, 237, 185, 218, + 94, 21, 70, 87, 167, 141, 157, 132, + 144, 216, 171, 0, 140, 188, 211, 10, + 247, 228, 88, 5, 184, 179, 69, 6, + 208, 44, 30, 143, 202, 63, 15, 2, + 193, 175, 189, 3, 1, 19, 138, 107, + 58, 145, 17, 65, 79, 103, 220, 234, + 151, 242, 207, 206, 240, 180, 230, 115, + 150, 172, 116, 34, 231, 173, 53, 133, + 226, 249, 55, 232, 28, 117, 223, 110, + 71, 241, 26, 113, 29, 41, 197, 137, + 111, 183, 98, 14, 170, 24, 190, 27, + 252, 86, 62, 75, 198, 210, 121, 32, + 154, 219, 192, 254, 120, 205, 90, 244, + 31, 221, 168, 51, 136, 7, 199, 49, + 177, 18, 16, 89, 39, 128, 236, 95, + 96, 81, 127, 169, 25, 181, 74, 13, + 45, 229, 122, 159, 147, 201, 156, 239, + 160, 224, 59, 77, 174, 42, 245, 176, + 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, + 225, 105, 20, 99, 85, 33, 12, 125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly int[] rcon = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + // precomputation tables of calculations for rounds + private static readonly int[] T0 = + { + unchecked((int) 0xa56363c6), unchecked((int) 0x847c7cf8), unchecked((int) 0x997777ee), unchecked((int) 0x8d7b7bf6), unchecked((int) 0x0df2f2ff), + unchecked((int) 0xbd6b6bd6), unchecked((int) 0xb16f6fde), unchecked((int) 0x54c5c591), unchecked((int) 0x50303060), unchecked((int) 0x03010102), + unchecked((int) 0xa96767ce), unchecked((int) 0x7d2b2b56), unchecked((int) 0x19fefee7), unchecked((int) 0x62d7d7b5), unchecked((int) 0xe6abab4d), + unchecked((int) 0x9a7676ec), unchecked((int) 0x45caca8f), unchecked((int) 0x9d82821f), unchecked((int) 0x40c9c989), unchecked((int) 0x877d7dfa), + unchecked((int) 0x15fafaef), unchecked((int) 0xeb5959b2), unchecked((int) 0xc947478e), unchecked((int) 0x0bf0f0fb), unchecked((int) 0xecadad41), + unchecked((int) 0x67d4d4b3), unchecked((int) 0xfda2a25f), unchecked((int) 0xeaafaf45), unchecked((int) 0xbf9c9c23), unchecked((int) 0xf7a4a453), + unchecked((int) 0x967272e4), unchecked((int) 0x5bc0c09b), unchecked((int) 0xc2b7b775), unchecked((int) 0x1cfdfde1), unchecked((int) 0xae93933d), + unchecked((int) 0x6a26264c), unchecked((int) 0x5a36366c), unchecked((int) 0x413f3f7e), unchecked((int) 0x02f7f7f5), unchecked((int) 0x4fcccc83), + unchecked((int) 0x5c343468), unchecked((int) 0xf4a5a551), unchecked((int) 0x34e5e5d1), unchecked((int) 0x08f1f1f9), unchecked((int) 0x937171e2), + unchecked((int) 0x73d8d8ab), unchecked((int) 0x53313162), unchecked((int) 0x3f15152a), unchecked((int) 0x0c040408), unchecked((int) 0x52c7c795), + unchecked((int) 0x65232346), unchecked((int) 0x5ec3c39d), unchecked((int) 0x28181830), unchecked((int) 0xa1969637), unchecked((int) 0x0f05050a), + unchecked((int) 0xb59a9a2f), unchecked((int) 0x0907070e), unchecked((int) 0x36121224), unchecked((int) 0x9b80801b), unchecked((int) 0x3de2e2df), + unchecked((int) 0x26ebebcd), unchecked((int) 0x6927274e), unchecked((int) 0xcdb2b27f), unchecked((int) 0x9f7575ea), unchecked((int) 0x1b090912), + unchecked((int) 0x9e83831d), unchecked((int) 0x742c2c58), unchecked((int) 0x2e1a1a34), unchecked((int) 0x2d1b1b36), unchecked((int) 0xb26e6edc), + unchecked((int) 0xee5a5ab4), unchecked((int) 0xfba0a05b), unchecked((int) 0xf65252a4), unchecked((int) 0x4d3b3b76), unchecked((int) 0x61d6d6b7), + unchecked((int) 0xceb3b37d), unchecked((int) 0x7b292952), unchecked((int) 0x3ee3e3dd), unchecked((int) 0x712f2f5e), unchecked((int) 0x97848413), + unchecked((int) 0xf55353a6), unchecked((int) 0x68d1d1b9), unchecked((int) 0x00000000), unchecked((int) 0x2cededc1), unchecked((int) 0x60202040), + unchecked((int) 0x1ffcfce3), unchecked((int) 0xc8b1b179), unchecked((int) 0xed5b5bb6), unchecked((int) 0xbe6a6ad4), unchecked((int) 0x46cbcb8d), + unchecked((int) 0xd9bebe67), unchecked((int) 0x4b393972), unchecked((int) 0xde4a4a94), unchecked((int) 0xd44c4c98), unchecked((int) 0xe85858b0), + unchecked((int) 0x4acfcf85), unchecked((int) 0x6bd0d0bb), unchecked((int) 0x2aefefc5), unchecked((int) 0xe5aaaa4f), unchecked((int) 0x16fbfbed), + unchecked((int) 0xc5434386), unchecked((int) 0xd74d4d9a), unchecked((int) 0x55333366), unchecked((int) 0x94858511), unchecked((int) 0xcf45458a), + unchecked((int) 0x10f9f9e9), unchecked((int) 0x06020204), unchecked((int) 0x817f7ffe), unchecked((int) 0xf05050a0), unchecked((int) 0x443c3c78), + unchecked((int) 0xba9f9f25), unchecked((int) 0xe3a8a84b), unchecked((int) 0xf35151a2), unchecked((int) 0xfea3a35d), unchecked((int) 0xc0404080), + unchecked((int) 0x8a8f8f05), unchecked((int) 0xad92923f), unchecked((int) 0xbc9d9d21), unchecked((int) 0x48383870), unchecked((int) 0x04f5f5f1), + unchecked((int) 0xdfbcbc63), unchecked((int) 0xc1b6b677), unchecked((int) 0x75dadaaf), unchecked((int) 0x63212142), unchecked((int) 0x30101020), + unchecked((int) 0x1affffe5), unchecked((int) 0x0ef3f3fd), unchecked((int) 0x6dd2d2bf), unchecked((int) 0x4ccdcd81), unchecked((int) 0x140c0c18), + unchecked((int) 0x35131326), unchecked((int) 0x2fececc3), unchecked((int) 0xe15f5fbe), unchecked((int) 0xa2979735), unchecked((int) 0xcc444488), + unchecked((int) 0x3917172e), unchecked((int) 0x57c4c493), unchecked((int) 0xf2a7a755), unchecked((int) 0x827e7efc), unchecked((int) 0x473d3d7a), + unchecked((int) 0xac6464c8), unchecked((int) 0xe75d5dba), unchecked((int) 0x2b191932), unchecked((int) 0x957373e6), unchecked((int) 0xa06060c0), + unchecked((int) 0x98818119), unchecked((int) 0xd14f4f9e), unchecked((int) 0x7fdcdca3), unchecked((int) 0x66222244), unchecked((int) 0x7e2a2a54), + unchecked((int) 0xab90903b), unchecked((int) 0x8388880b), unchecked((int) 0xca46468c), unchecked((int) 0x29eeeec7), unchecked((int) 0xd3b8b86b), + unchecked((int) 0x3c141428), unchecked((int) 0x79dedea7), unchecked((int) 0xe25e5ebc), unchecked((int) 0x1d0b0b16), unchecked((int) 0x76dbdbad), + unchecked((int) 0x3be0e0db), unchecked((int) 0x56323264), unchecked((int) 0x4e3a3a74), unchecked((int) 0x1e0a0a14), unchecked((int) 0xdb494992), + unchecked((int) 0x0a06060c), unchecked((int) 0x6c242448), unchecked((int) 0xe45c5cb8), unchecked((int) 0x5dc2c29f), unchecked((int) 0x6ed3d3bd), + unchecked((int) 0xefacac43), unchecked((int) 0xa66262c4), unchecked((int) 0xa8919139), unchecked((int) 0xa4959531), unchecked((int) 0x37e4e4d3), + unchecked((int) 0x8b7979f2), unchecked((int) 0x32e7e7d5), unchecked((int) 0x43c8c88b), unchecked((int) 0x5937376e), unchecked((int) 0xb76d6dda), + unchecked((int) 0x8c8d8d01), unchecked((int) 0x64d5d5b1), unchecked((int) 0xd24e4e9c), unchecked((int) 0xe0a9a949), unchecked((int) 0xb46c6cd8), + unchecked((int) 0xfa5656ac), unchecked((int) 0x07f4f4f3), unchecked((int) 0x25eaeacf), unchecked((int) 0xaf6565ca), unchecked((int) 0x8e7a7af4), + unchecked((int) 0xe9aeae47), unchecked((int) 0x18080810), unchecked((int) 0xd5baba6f), unchecked((int) 0x887878f0), unchecked((int) 0x6f25254a), + unchecked((int) 0x722e2e5c), unchecked((int) 0x241c1c38), unchecked((int) 0xf1a6a657), unchecked((int) 0xc7b4b473), unchecked((int) 0x51c6c697), + unchecked((int) 0x23e8e8cb), unchecked((int) 0x7cdddda1), unchecked((int) 0x9c7474e8), unchecked((int) 0x211f1f3e), unchecked((int) 0xdd4b4b96), + unchecked((int) 0xdcbdbd61), unchecked((int) 0x868b8b0d), unchecked((int) 0x858a8a0f), unchecked((int) 0x907070e0), unchecked((int) 0x423e3e7c), + unchecked((int) 0xc4b5b571), unchecked((int) 0xaa6666cc), unchecked((int) 0xd8484890), unchecked((int) 0x05030306), unchecked((int) 0x01f6f6f7), + unchecked((int) 0x120e0e1c), unchecked((int) 0xa36161c2), unchecked((int) 0x5f35356a), unchecked((int) 0xf95757ae), unchecked((int) 0xd0b9b969), + unchecked((int) 0x91868617), unchecked((int) 0x58c1c199), unchecked((int) 0x271d1d3a), unchecked((int) 0xb99e9e27), unchecked((int) 0x38e1e1d9), + unchecked((int) 0x13f8f8eb), unchecked((int) 0xb398982b), unchecked((int) 0x33111122), unchecked((int) 0xbb6969d2), unchecked((int) 0x70d9d9a9), + unchecked((int) 0x898e8e07), unchecked((int) 0xa7949433), unchecked((int) 0xb69b9b2d), unchecked((int) 0x221e1e3c), unchecked((int) 0x92878715), + unchecked((int) 0x20e9e9c9), unchecked((int) 0x49cece87), unchecked((int) 0xff5555aa), unchecked((int) 0x78282850), unchecked((int) 0x7adfdfa5), + unchecked((int) 0x8f8c8c03), unchecked((int) 0xf8a1a159), unchecked((int) 0x80898909), unchecked((int) 0x170d0d1a), unchecked((int) 0xdabfbf65), + unchecked((int) 0x31e6e6d7), unchecked((int) 0xc6424284), unchecked((int) 0xb86868d0), unchecked((int) 0xc3414182), unchecked((int) 0xb0999929), + unchecked((int) 0x772d2d5a), unchecked((int) 0x110f0f1e), unchecked((int) 0xcbb0b07b), unchecked((int) 0xfc5454a8), unchecked((int) 0xd6bbbb6d), + unchecked((int) 0x3a16162c) + }; + + private static readonly int[] Tinv0 = + { + unchecked((int) 0x50a7f451), unchecked((int) 0x5365417e), unchecked((int) 0xc3a4171a), unchecked((int) 0x965e273a), unchecked((int) 0xcb6bab3b), + unchecked((int) 0xf1459d1f), unchecked((int) 0xab58faac), unchecked((int) 0x9303e34b), unchecked((int) 0x55fa3020), unchecked((int) 0xf66d76ad), + unchecked((int) 0x9176cc88), unchecked((int) 0x254c02f5), unchecked((int) 0xfcd7e54f), unchecked((int) 0xd7cb2ac5), unchecked((int) 0x80443526), + unchecked((int) 0x8fa362b5), unchecked((int) 0x495ab1de), unchecked((int) 0x671bba25), unchecked((int) 0x980eea45), unchecked((int) 0xe1c0fe5d), + unchecked((int) 0x02752fc3), unchecked((int) 0x12f04c81), unchecked((int) 0xa397468d), unchecked((int) 0xc6f9d36b), unchecked((int) 0xe75f8f03), + unchecked((int) 0x959c9215), unchecked((int) 0xeb7a6dbf), unchecked((int) 0xda595295), unchecked((int) 0x2d83bed4), unchecked((int) 0xd3217458), + unchecked((int) 0x2969e049), unchecked((int) 0x44c8c98e), unchecked((int) 0x6a89c275), unchecked((int) 0x78798ef4), unchecked((int) 0x6b3e5899), + unchecked((int) 0xdd71b927), unchecked((int) 0xb64fe1be), unchecked((int) 0x17ad88f0), unchecked((int) 0x66ac20c9), unchecked((int) 0xb43ace7d), + unchecked((int) 0x184adf63), unchecked((int) 0x82311ae5), unchecked((int) 0x60335197), unchecked((int) 0x457f5362), unchecked((int) 0xe07764b1), + unchecked((int) 0x84ae6bbb), unchecked((int) 0x1ca081fe), unchecked((int) 0x942b08f9), unchecked((int) 0x58684870), unchecked((int) 0x19fd458f), + unchecked((int) 0x876cde94), unchecked((int) 0xb7f87b52), unchecked((int) 0x23d373ab), unchecked((int) 0xe2024b72), unchecked((int) 0x578f1fe3), + unchecked((int) 0x2aab5566), unchecked((int) 0x0728ebb2), unchecked((int) 0x03c2b52f), unchecked((int) 0x9a7bc586), unchecked((int) 0xa50837d3), + unchecked((int) 0xf2872830), unchecked((int) 0xb2a5bf23), unchecked((int) 0xba6a0302), unchecked((int) 0x5c8216ed), unchecked((int) 0x2b1ccf8a), + unchecked((int) 0x92b479a7), unchecked((int) 0xf0f207f3), unchecked((int) 0xa1e2694e), unchecked((int) 0xcdf4da65), unchecked((int) 0xd5be0506), + unchecked((int) 0x1f6234d1), unchecked((int) 0x8afea6c4), unchecked((int) 0x9d532e34), unchecked((int) 0xa055f3a2), unchecked((int) 0x32e18a05), + unchecked((int) 0x75ebf6a4), unchecked((int) 0x39ec830b), unchecked((int) 0xaaef6040), unchecked((int) 0x069f715e), unchecked((int) 0x51106ebd), + unchecked((int) 0xf98a213e), unchecked((int) 0x3d06dd96), unchecked((int) 0xae053edd), unchecked((int) 0x46bde64d), unchecked((int) 0xb58d5491), + unchecked((int) 0x055dc471), unchecked((int) 0x6fd40604), unchecked((int) 0xff155060), unchecked((int) 0x24fb9819), unchecked((int) 0x97e9bdd6), + unchecked((int) 0xcc434089), unchecked((int) 0x779ed967), unchecked((int) 0xbd42e8b0), unchecked((int) 0x888b8907), unchecked((int) 0x385b19e7), + unchecked((int) 0xdbeec879), unchecked((int) 0x470a7ca1), unchecked((int) 0xe90f427c), unchecked((int) 0xc91e84f8), unchecked((int) 0x00000000), + unchecked((int) 0x83868009), unchecked((int) 0x48ed2b32), unchecked((int) 0xac70111e), unchecked((int) 0x4e725a6c), unchecked((int) 0xfbff0efd), + unchecked((int) 0x5638850f), unchecked((int) 0x1ed5ae3d), unchecked((int) 0x27392d36), unchecked((int) 0x64d90f0a), unchecked((int) 0x21a65c68), + unchecked((int) 0xd1545b9b), unchecked((int) 0x3a2e3624), unchecked((int) 0xb1670a0c), unchecked((int) 0x0fe75793), unchecked((int) 0xd296eeb4), + unchecked((int) 0x9e919b1b), unchecked((int) 0x4fc5c080), unchecked((int) 0xa220dc61), unchecked((int) 0x694b775a), unchecked((int) 0x161a121c), + unchecked((int) 0x0aba93e2), unchecked((int) 0xe52aa0c0), unchecked((int) 0x43e0223c), unchecked((int) 0x1d171b12), unchecked((int) 0x0b0d090e), + unchecked((int) 0xadc78bf2), unchecked((int) 0xb9a8b62d), unchecked((int) 0xc8a91e14), unchecked((int) 0x8519f157), unchecked((int) 0x4c0775af), + unchecked((int) 0xbbdd99ee), unchecked((int) 0xfd607fa3), unchecked((int) 0x9f2601f7), unchecked((int) 0xbcf5725c), unchecked((int) 0xc53b6644), + unchecked((int) 0x347efb5b), unchecked((int) 0x7629438b), unchecked((int) 0xdcc623cb), unchecked((int) 0x68fcedb6), unchecked((int) 0x63f1e4b8), + unchecked((int) 0xcadc31d7), unchecked((int) 0x10856342), unchecked((int) 0x40229713), unchecked((int) 0x2011c684), unchecked((int) 0x7d244a85), + unchecked((int) 0xf83dbbd2), unchecked((int) 0x1132f9ae), unchecked((int) 0x6da129c7), unchecked((int) 0x4b2f9e1d), unchecked((int) 0xf330b2dc), + unchecked((int) 0xec52860d), unchecked((int) 0xd0e3c177), unchecked((int) 0x6c16b32b), unchecked((int) 0x99b970a9), unchecked((int) 0xfa489411), + unchecked((int) 0x2264e947), unchecked((int) 0xc48cfca8), unchecked((int) 0x1a3ff0a0), unchecked((int) 0xd82c7d56), unchecked((int) 0xef903322), + unchecked((int) 0xc74e4987), unchecked((int) 0xc1d138d9), unchecked((int) 0xfea2ca8c), unchecked((int) 0x360bd498), unchecked((int) 0xcf81f5a6), + unchecked((int) 0x28de7aa5), unchecked((int) 0x268eb7da), unchecked((int) 0xa4bfad3f), unchecked((int) 0xe49d3a2c), unchecked((int) 0x0d927850), + unchecked((int) 0x9bcc5f6a), unchecked((int) 0x62467e54), unchecked((int) 0xc2138df6), unchecked((int) 0xe8b8d890), unchecked((int) 0x5ef7392e), + unchecked((int) 0xf5afc382), unchecked((int) 0xbe805d9f), unchecked((int) 0x7c93d069), unchecked((int) 0xa92dd56f), unchecked((int) 0xb31225cf), + unchecked((int) 0x3b99acc8), unchecked((int) 0xa77d1810), unchecked((int) 0x6e639ce8), unchecked((int) 0x7bbb3bdb), unchecked((int) 0x097826cd), + unchecked((int) 0xf418596e), unchecked((int) 0x01b79aec), unchecked((int) 0xa89a4f83), unchecked((int) 0x656e95e6), unchecked((int) 0x7ee6ffaa), + unchecked((int) 0x08cfbc21), unchecked((int) 0xe6e815ef), unchecked((int) 0xd99be7ba), unchecked((int) 0xce366f4a), unchecked((int) 0xd4099fea), + unchecked((int) 0xd67cb029), unchecked((int) 0xafb2a431), unchecked((int) 0x31233f2a), unchecked((int) 0x3094a5c6), unchecked((int) 0xc066a235), + unchecked((int) 0x37bc4e74), unchecked((int) 0xa6ca82fc), unchecked((int) 0xb0d090e0), unchecked((int) 0x15d8a733), unchecked((int) 0x4a9804f1), + unchecked((int) 0xf7daec41), unchecked((int) 0x0e50cd7f), unchecked((int) 0x2ff69117), unchecked((int) 0x8dd64d76), unchecked((int) 0x4db0ef43), + unchecked((int) 0x544daacc), unchecked((int) 0xdf0496e4), unchecked((int) 0xe3b5d19e), unchecked((int) 0x1b886a4c), unchecked((int) 0xb81f2cc1), + unchecked((int) 0x7f516546), unchecked((int) 0x04ea5e9d), unchecked((int) 0x5d358c01), unchecked((int) 0x737487fa), unchecked((int) 0x2e410bfb), + unchecked((int) 0x5a1d67b3), unchecked((int) 0x52d2db92), unchecked((int) 0x335610e9), unchecked((int) 0x1347d66d), unchecked((int) 0x8c61d79a), + unchecked((int) 0x7a0ca137), unchecked((int) 0x8e14f859), unchecked((int) 0x893c13eb), unchecked((int) 0xee27a9ce), unchecked((int) 0x35c961b7), + unchecked((int) 0xede51ce1), unchecked((int) 0x3cb1477a), unchecked((int) 0x59dfd29c), unchecked((int) 0x3f73f255), unchecked((int) 0x79ce1418), + unchecked((int) 0xbf37c773), unchecked((int) 0xeacdf753), unchecked((int) 0x5baafd5f), unchecked((int) 0x146f3ddf), unchecked((int) 0x86db4478), + unchecked((int) 0x81f3afca), unchecked((int) 0x3ec468b9), unchecked((int) 0x2c342438), unchecked((int) 0x5f40a3c2), unchecked((int) 0x72c31d16), + unchecked((int) 0x0c25e2bc), unchecked((int) 0x8b493c28), unchecked((int) 0x41950dff), unchecked((int) 0x7101a839), unchecked((int) 0xdeb30c08), + unchecked((int) 0x9ce4b4d8), unchecked((int) 0x90c15664), unchecked((int) 0x6184cb7b), unchecked((int) 0x70b632d5), unchecked((int) 0x745c6c48), + unchecked((int) 0x4257b8d0) + }; + + private int Shift( + int r, + int shift) + { + return ((int)(((uint) r >> shift) | (uint)(r << (32 - shift)))); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const int m1 = unchecked((int) 0x80808080); + private const int m2 = unchecked((int) 0x7f7f7f7f); + private const int m3 = unchecked((int) 0x0000001b); + + private int FFmulX( + int x) + { + return ((int) (((x & m2) << 1) ^ (( (uint) (x & m1) >> 7) * m3))); + } + + + /* + The following defines provide alternative definitions of FFmulX that might + give improved performance if a fast 32-bit multiply is not available. + + private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } + private static final int m4 = 0x1b1b1b1b; + private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } + + */ + + private int Inv_Mcol( + int x) + { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + private 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); + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on key size and block size + * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits + * This code is written assuming those are the only possible values + */ + private int[,] GenerateWorkingKey( + byte[] key, + bool forEncryption) + { + int KC = key.Length / 4; // key length in words + int t; + + if ((KC != 4) && (KC != 6) && (KC != 8)) { + throw new ArgumentException("Key length not 128/192/256 bits."); + } + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + int[,] W = new int[ROUNDS+1, 4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + for (int i = 0; i < key.Length; t++) + { + W[t >> 2, t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + int temp = W[(i-1)>>2, (i-1)&3]; + if ((i % KC) == 0) { + temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC)-1]; + } else if ((KC > 6) && ((i % KC) == 4)) { + temp = SubWord(temp); + } + + W[i>>2, i&3] = W[(i - KC)>>2, (i-KC)&3] ^ temp; + } + + if (!forEncryption) + { + for (int j = 1; j < ROUNDS; j++) + { + for (int i = 0; i < 4; i++) + { + W[j, i] = Inv_Mcol(W[j, i]); + } + } + } + + return W; + } + + private int ROUNDS; + private int[,] WorkingKey; + private int C0, C1, C2, C3; + private bool forEncryption; + + private const int BLOCK_SIZE = 16; + + /** + * default constructor - 128 bit block size. + */ + public AesEngine() + { + } + + /** + * initialise an AES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + KeyParameter keyParameter = parameters as KeyParameter; + + if (keyParameter == null) + throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().Name); + + WorkingKey = GenerateWorkingKey(keyParameter.GetKey(), forEncryption); + + this.forEncryption = forEncryption; + } + + public string AlgorithmName + { + get { return "AES"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (WorkingKey == null) + { + throw new InvalidOperationException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + UnPackBlock(input, inOff); + + if (forEncryption) + { + EncryptBlock(WorkingKey); + } + else + { + DecryptBlock(WorkingKey); + } + + PackBlock(output, outOff); + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + 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; + } + + 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); + } + + 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]; + + for (r = 1; 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[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]; + + // 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]; + } + + 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]; + + for (r = ROUNDS-1; 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[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]; + + // 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]; + } + } +} diff --git a/src/core/srcbc/crypto/engines/AesFastEngine.cs b/src/core/srcbc/crypto/engines/AesFastEngine.cs new file mode 100644 index 0000000..22ef651 --- /dev/null +++ b/src/core/srcbc/crypto/engines/AesFastEngine.cs @@ -0,0 +1,865 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of the AES (Rijndael)), from FIPS-197. + *

+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * There are three levels of tradeoff of speed vs memory + * Because java has no preprocessor), they are written as three separate classes from which to choose + * + * The fastest uses 8Kbytes of static tables to precompute round calculations), 4 256 word tables for encryption + * and 4 for decryption. + * + * The middle performance version uses only one 256 word table for each), for a total of 2Kbytes), + * adding 12 rotate operations per round to compute the values contained in the other tables from + * the contents of the first + * + * The slowest version uses no static tables at all and computes the values in each round + *

+ *

+ * This file contains the fast version with 8Kbytes of static tables for round precomputation + *

+ */ + public class AesFastEngine + : IBlockCipher + { + // The S box + private static readonly byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, + (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, + (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, + (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, + (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, + (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, + (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, + (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, + (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, + (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, + (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, + (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, + (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, + (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, + (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, + (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, + (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + // The inverse S-box + private static readonly byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, + (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, + (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, + (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, + (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, + (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, + (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, + (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, + (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, + (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, + (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, + (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, + (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, + (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, + (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, + (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, + (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly int[] rcon = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + // precomputation tables of calculations for rounds + private static readonly int[] T0 = + { + unchecked((int) 0xa56363c6), unchecked((int) 0x847c7cf8), unchecked((int) 0x997777ee), unchecked((int) 0x8d7b7bf6), unchecked((int) 0x0df2f2ff), + unchecked((int) 0xbd6b6bd6), unchecked((int) 0xb16f6fde), unchecked((int) 0x54c5c591), unchecked((int) 0x50303060), unchecked((int) 0x03010102), + unchecked((int) 0xa96767ce), unchecked((int) 0x7d2b2b56), unchecked((int) 0x19fefee7), unchecked((int) 0x62d7d7b5), unchecked((int) 0xe6abab4d), + unchecked((int) 0x9a7676ec), unchecked((int) 0x45caca8f), unchecked((int) 0x9d82821f), unchecked((int) 0x40c9c989), unchecked((int) 0x877d7dfa), + unchecked((int) 0x15fafaef), unchecked((int) 0xeb5959b2), unchecked((int) 0xc947478e), unchecked((int) 0x0bf0f0fb), unchecked((int) 0xecadad41), + unchecked((int) 0x67d4d4b3), unchecked((int) 0xfda2a25f), unchecked((int) 0xeaafaf45), unchecked((int) 0xbf9c9c23), unchecked((int) 0xf7a4a453), + unchecked((int) 0x967272e4), unchecked((int) 0x5bc0c09b), unchecked((int) 0xc2b7b775), unchecked((int) 0x1cfdfde1), unchecked((int) 0xae93933d), + unchecked((int) 0x6a26264c), unchecked((int) 0x5a36366c), unchecked((int) 0x413f3f7e), unchecked((int) 0x02f7f7f5), unchecked((int) 0x4fcccc83), + unchecked((int) 0x5c343468), unchecked((int) 0xf4a5a551), unchecked((int) 0x34e5e5d1), unchecked((int) 0x08f1f1f9), unchecked((int) 0x937171e2), + unchecked((int) 0x73d8d8ab), unchecked((int) 0x53313162), unchecked((int) 0x3f15152a), unchecked((int) 0x0c040408), unchecked((int) 0x52c7c795), + unchecked((int) 0x65232346), unchecked((int) 0x5ec3c39d), unchecked((int) 0x28181830), unchecked((int) 0xa1969637), unchecked((int) 0x0f05050a), + unchecked((int) 0xb59a9a2f), unchecked((int) 0x0907070e), unchecked((int) 0x36121224), unchecked((int) 0x9b80801b), unchecked((int) 0x3de2e2df), + unchecked((int) 0x26ebebcd), unchecked((int) 0x6927274e), unchecked((int) 0xcdb2b27f), unchecked((int) 0x9f7575ea), unchecked((int) 0x1b090912), + unchecked((int) 0x9e83831d), unchecked((int) 0x742c2c58), unchecked((int) 0x2e1a1a34), unchecked((int) 0x2d1b1b36), unchecked((int) 0xb26e6edc), + unchecked((int) 0xee5a5ab4), unchecked((int) 0xfba0a05b), unchecked((int) 0xf65252a4), unchecked((int) 0x4d3b3b76), unchecked((int) 0x61d6d6b7), + unchecked((int) 0xceb3b37d), unchecked((int) 0x7b292952), unchecked((int) 0x3ee3e3dd), unchecked((int) 0x712f2f5e), unchecked((int) 0x97848413), + unchecked((int) 0xf55353a6), unchecked((int) 0x68d1d1b9), unchecked((int) 0x00000000), unchecked((int) 0x2cededc1), unchecked((int) 0x60202040), + unchecked((int) 0x1ffcfce3), unchecked((int) 0xc8b1b179), unchecked((int) 0xed5b5bb6), unchecked((int) 0xbe6a6ad4), unchecked((int) 0x46cbcb8d), + unchecked((int) 0xd9bebe67), unchecked((int) 0x4b393972), unchecked((int) 0xde4a4a94), unchecked((int) 0xd44c4c98), unchecked((int) 0xe85858b0), + unchecked((int) 0x4acfcf85), unchecked((int) 0x6bd0d0bb), unchecked((int) 0x2aefefc5), unchecked((int) 0xe5aaaa4f), unchecked((int) 0x16fbfbed), + unchecked((int) 0xc5434386), unchecked((int) 0xd74d4d9a), unchecked((int) 0x55333366), unchecked((int) 0x94858511), unchecked((int) 0xcf45458a), + unchecked((int) 0x10f9f9e9), unchecked((int) 0x06020204), unchecked((int) 0x817f7ffe), unchecked((int) 0xf05050a0), unchecked((int) 0x443c3c78), + unchecked((int) 0xba9f9f25), unchecked((int) 0xe3a8a84b), unchecked((int) 0xf35151a2), unchecked((int) 0xfea3a35d), unchecked((int) 0xc0404080), + unchecked((int) 0x8a8f8f05), unchecked((int) 0xad92923f), unchecked((int) 0xbc9d9d21), unchecked((int) 0x48383870), unchecked((int) 0x04f5f5f1), + unchecked((int) 0xdfbcbc63), unchecked((int) 0xc1b6b677), unchecked((int) 0x75dadaaf), unchecked((int) 0x63212142), unchecked((int) 0x30101020), + unchecked((int) 0x1affffe5), unchecked((int) 0x0ef3f3fd), unchecked((int) 0x6dd2d2bf), unchecked((int) 0x4ccdcd81), unchecked((int) 0x140c0c18), + unchecked((int) 0x35131326), unchecked((int) 0x2fececc3), unchecked((int) 0xe15f5fbe), unchecked((int) 0xa2979735), unchecked((int) 0xcc444488), + unchecked((int) 0x3917172e), unchecked((int) 0x57c4c493), unchecked((int) 0xf2a7a755), unchecked((int) 0x827e7efc), unchecked((int) 0x473d3d7a), + unchecked((int) 0xac6464c8), unchecked((int) 0xe75d5dba), unchecked((int) 0x2b191932), unchecked((int) 0x957373e6), unchecked((int) 0xa06060c0), + unchecked((int) 0x98818119), unchecked((int) 0xd14f4f9e), unchecked((int) 0x7fdcdca3), unchecked((int) 0x66222244), unchecked((int) 0x7e2a2a54), + unchecked((int) 0xab90903b), unchecked((int) 0x8388880b), unchecked((int) 0xca46468c), unchecked((int) 0x29eeeec7), unchecked((int) 0xd3b8b86b), + unchecked((int) 0x3c141428), unchecked((int) 0x79dedea7), unchecked((int) 0xe25e5ebc), unchecked((int) 0x1d0b0b16), unchecked((int) 0x76dbdbad), + unchecked((int) 0x3be0e0db), unchecked((int) 0x56323264), unchecked((int) 0x4e3a3a74), unchecked((int) 0x1e0a0a14), unchecked((int) 0xdb494992), + unchecked((int) 0x0a06060c), unchecked((int) 0x6c242448), unchecked((int) 0xe45c5cb8), unchecked((int) 0x5dc2c29f), unchecked((int) 0x6ed3d3bd), + unchecked((int) 0xefacac43), unchecked((int) 0xa66262c4), unchecked((int) 0xa8919139), unchecked((int) 0xa4959531), unchecked((int) 0x37e4e4d3), + unchecked((int) 0x8b7979f2), unchecked((int) 0x32e7e7d5), unchecked((int) 0x43c8c88b), unchecked((int) 0x5937376e), unchecked((int) 0xb76d6dda), + unchecked((int) 0x8c8d8d01), unchecked((int) 0x64d5d5b1), unchecked((int) 0xd24e4e9c), unchecked((int) 0xe0a9a949), unchecked((int) 0xb46c6cd8), + unchecked((int) 0xfa5656ac), unchecked((int) 0x07f4f4f3), unchecked((int) 0x25eaeacf), unchecked((int) 0xaf6565ca), unchecked((int) 0x8e7a7af4), + unchecked((int) 0xe9aeae47), unchecked((int) 0x18080810), unchecked((int) 0xd5baba6f), unchecked((int) 0x887878f0), unchecked((int) 0x6f25254a), + unchecked((int) 0x722e2e5c), unchecked((int) 0x241c1c38), unchecked((int) 0xf1a6a657), unchecked((int) 0xc7b4b473), unchecked((int) 0x51c6c697), + unchecked((int) 0x23e8e8cb), unchecked((int) 0x7cdddda1), unchecked((int) 0x9c7474e8), unchecked((int) 0x211f1f3e), unchecked((int) 0xdd4b4b96), + unchecked((int) 0xdcbdbd61), unchecked((int) 0x868b8b0d), unchecked((int) 0x858a8a0f), unchecked((int) 0x907070e0), unchecked((int) 0x423e3e7c), + unchecked((int) 0xc4b5b571), unchecked((int) 0xaa6666cc), unchecked((int) 0xd8484890), unchecked((int) 0x05030306), unchecked((int) 0x01f6f6f7), + unchecked((int) 0x120e0e1c), unchecked((int) 0xa36161c2), unchecked((int) 0x5f35356a), unchecked((int) 0xf95757ae), unchecked((int) 0xd0b9b969), + unchecked((int) 0x91868617), unchecked((int) 0x58c1c199), unchecked((int) 0x271d1d3a), unchecked((int) 0xb99e9e27), unchecked((int) 0x38e1e1d9), + unchecked((int) 0x13f8f8eb), unchecked((int) 0xb398982b), unchecked((int) 0x33111122), unchecked((int) 0xbb6969d2), unchecked((int) 0x70d9d9a9), + unchecked((int) 0x898e8e07), unchecked((int) 0xa7949433), unchecked((int) 0xb69b9b2d), unchecked((int) 0x221e1e3c), unchecked((int) 0x92878715), + unchecked((int) 0x20e9e9c9), unchecked((int) 0x49cece87), unchecked((int) 0xff5555aa), unchecked((int) 0x78282850), unchecked((int) 0x7adfdfa5), + unchecked((int) 0x8f8c8c03), unchecked((int) 0xf8a1a159), unchecked((int) 0x80898909), unchecked((int) 0x170d0d1a), unchecked((int) 0xdabfbf65), + unchecked((int) 0x31e6e6d7), unchecked((int) 0xc6424284), unchecked((int) 0xb86868d0), unchecked((int) 0xc3414182), unchecked((int) 0xb0999929), + unchecked((int) 0x772d2d5a), unchecked((int) 0x110f0f1e), unchecked((int) 0xcbb0b07b), unchecked((int) 0xfc5454a8), unchecked((int) 0xd6bbbb6d), + unchecked((int) 0x3a16162c)}; + + private static readonly int[] T1 = + { + unchecked((int) 0x6363c6a5), unchecked((int) 0x7c7cf884), unchecked((int) 0x7777ee99), unchecked((int) 0x7b7bf68d), unchecked((int) 0xf2f2ff0d), + unchecked((int) 0x6b6bd6bd), unchecked((int) 0x6f6fdeb1), unchecked((int) 0xc5c59154), unchecked((int) 0x30306050), unchecked((int) 0x01010203), + unchecked((int) 0x6767cea9), unchecked((int) 0x2b2b567d), unchecked((int) 0xfefee719), unchecked((int) 0xd7d7b562), unchecked((int) 0xabab4de6), + unchecked((int) 0x7676ec9a), unchecked((int) 0xcaca8f45), unchecked((int) 0x82821f9d), unchecked((int) 0xc9c98940), unchecked((int) 0x7d7dfa87), + unchecked((int) 0xfafaef15), unchecked((int) 0x5959b2eb), unchecked((int) 0x47478ec9), unchecked((int) 0xf0f0fb0b), unchecked((int) 0xadad41ec), + unchecked((int) 0xd4d4b367), unchecked((int) 0xa2a25ffd), unchecked((int) 0xafaf45ea), unchecked((int) 0x9c9c23bf), unchecked((int) 0xa4a453f7), + unchecked((int) 0x7272e496), unchecked((int) 0xc0c09b5b), unchecked((int) 0xb7b775c2), unchecked((int) 0xfdfde11c), unchecked((int) 0x93933dae), + unchecked((int) 0x26264c6a), unchecked((int) 0x36366c5a), unchecked((int) 0x3f3f7e41), unchecked((int) 0xf7f7f502), unchecked((int) 0xcccc834f), + unchecked((int) 0x3434685c), unchecked((int) 0xa5a551f4), unchecked((int) 0xe5e5d134), unchecked((int) 0xf1f1f908), unchecked((int) 0x7171e293), + unchecked((int) 0xd8d8ab73), unchecked((int) 0x31316253), unchecked((int) 0x15152a3f), unchecked((int) 0x0404080c), unchecked((int) 0xc7c79552), + unchecked((int) 0x23234665), unchecked((int) 0xc3c39d5e), unchecked((int) 0x18183028), unchecked((int) 0x969637a1), unchecked((int) 0x05050a0f), + unchecked((int) 0x9a9a2fb5), unchecked((int) 0x07070e09), unchecked((int) 0x12122436), unchecked((int) 0x80801b9b), unchecked((int) 0xe2e2df3d), + unchecked((int) 0xebebcd26), unchecked((int) 0x27274e69), unchecked((int) 0xb2b27fcd), unchecked((int) 0x7575ea9f), unchecked((int) 0x0909121b), + unchecked((int) 0x83831d9e), unchecked((int) 0x2c2c5874), unchecked((int) 0x1a1a342e), unchecked((int) 0x1b1b362d), unchecked((int) 0x6e6edcb2), + unchecked((int) 0x5a5ab4ee), unchecked((int) 0xa0a05bfb), unchecked((int) 0x5252a4f6), unchecked((int) 0x3b3b764d), unchecked((int) 0xd6d6b761), + unchecked((int) 0xb3b37dce), unchecked((int) 0x2929527b), unchecked((int) 0xe3e3dd3e), unchecked((int) 0x2f2f5e71), unchecked((int) 0x84841397), + unchecked((int) 0x5353a6f5), unchecked((int) 0xd1d1b968), unchecked((int) 0x00000000), unchecked((int) 0xededc12c), unchecked((int) 0x20204060), + unchecked((int) 0xfcfce31f), unchecked((int) 0xb1b179c8), unchecked((int) 0x5b5bb6ed), unchecked((int) 0x6a6ad4be), unchecked((int) 0xcbcb8d46), + unchecked((int) 0xbebe67d9), unchecked((int) 0x3939724b), unchecked((int) 0x4a4a94de), unchecked((int) 0x4c4c98d4), unchecked((int) 0x5858b0e8), + unchecked((int) 0xcfcf854a), unchecked((int) 0xd0d0bb6b), unchecked((int) 0xefefc52a), unchecked((int) 0xaaaa4fe5), unchecked((int) 0xfbfbed16), + unchecked((int) 0x434386c5), unchecked((int) 0x4d4d9ad7), unchecked((int) 0x33336655), unchecked((int) 0x85851194), unchecked((int) 0x45458acf), + unchecked((int) 0xf9f9e910), unchecked((int) 0x02020406), unchecked((int) 0x7f7ffe81), unchecked((int) 0x5050a0f0), unchecked((int) 0x3c3c7844), + unchecked((int) 0x9f9f25ba), unchecked((int) 0xa8a84be3), unchecked((int) 0x5151a2f3), unchecked((int) 0xa3a35dfe), unchecked((int) 0x404080c0), + unchecked((int) 0x8f8f058a), unchecked((int) 0x92923fad), unchecked((int) 0x9d9d21bc), unchecked((int) 0x38387048), unchecked((int) 0xf5f5f104), + unchecked((int) 0xbcbc63df), unchecked((int) 0xb6b677c1), unchecked((int) 0xdadaaf75), unchecked((int) 0x21214263), unchecked((int) 0x10102030), + unchecked((int) 0xffffe51a), unchecked((int) 0xf3f3fd0e), unchecked((int) 0xd2d2bf6d), unchecked((int) 0xcdcd814c), unchecked((int) 0x0c0c1814), + unchecked((int) 0x13132635), unchecked((int) 0xececc32f), unchecked((int) 0x5f5fbee1), unchecked((int) 0x979735a2), unchecked((int) 0x444488cc), + unchecked((int) 0x17172e39), unchecked((int) 0xc4c49357), unchecked((int) 0xa7a755f2), unchecked((int) 0x7e7efc82), unchecked((int) 0x3d3d7a47), + unchecked((int) 0x6464c8ac), unchecked((int) 0x5d5dbae7), unchecked((int) 0x1919322b), unchecked((int) 0x7373e695), unchecked((int) 0x6060c0a0), + unchecked((int) 0x81811998), unchecked((int) 0x4f4f9ed1), unchecked((int) 0xdcdca37f), unchecked((int) 0x22224466), unchecked((int) 0x2a2a547e), + unchecked((int) 0x90903bab), unchecked((int) 0x88880b83), unchecked((int) 0x46468cca), unchecked((int) 0xeeeec729), unchecked((int) 0xb8b86bd3), + unchecked((int) 0x1414283c), unchecked((int) 0xdedea779), unchecked((int) 0x5e5ebce2), unchecked((int) 0x0b0b161d), unchecked((int) 0xdbdbad76), + unchecked((int) 0xe0e0db3b), unchecked((int) 0x32326456), unchecked((int) 0x3a3a744e), unchecked((int) 0x0a0a141e), unchecked((int) 0x494992db), + unchecked((int) 0x06060c0a), unchecked((int) 0x2424486c), unchecked((int) 0x5c5cb8e4), unchecked((int) 0xc2c29f5d), unchecked((int) 0xd3d3bd6e), + unchecked((int) 0xacac43ef), unchecked((int) 0x6262c4a6), unchecked((int) 0x919139a8), unchecked((int) 0x959531a4), unchecked((int) 0xe4e4d337), + unchecked((int) 0x7979f28b), unchecked((int) 0xe7e7d532), unchecked((int) 0xc8c88b43), unchecked((int) 0x37376e59), unchecked((int) 0x6d6ddab7), + unchecked((int) 0x8d8d018c), unchecked((int) 0xd5d5b164), unchecked((int) 0x4e4e9cd2), unchecked((int) 0xa9a949e0), unchecked((int) 0x6c6cd8b4), + unchecked((int) 0x5656acfa), unchecked((int) 0xf4f4f307), unchecked((int) 0xeaeacf25), unchecked((int) 0x6565caaf), unchecked((int) 0x7a7af48e), + unchecked((int) 0xaeae47e9), unchecked((int) 0x08081018), unchecked((int) 0xbaba6fd5), unchecked((int) 0x7878f088), unchecked((int) 0x25254a6f), + unchecked((int) 0x2e2e5c72), unchecked((int) 0x1c1c3824), unchecked((int) 0xa6a657f1), unchecked((int) 0xb4b473c7), unchecked((int) 0xc6c69751), + unchecked((int) 0xe8e8cb23), unchecked((int) 0xdddda17c), unchecked((int) 0x7474e89c), unchecked((int) 0x1f1f3e21), unchecked((int) 0x4b4b96dd), + unchecked((int) 0xbdbd61dc), unchecked((int) 0x8b8b0d86), unchecked((int) 0x8a8a0f85), unchecked((int) 0x7070e090), unchecked((int) 0x3e3e7c42), + unchecked((int) 0xb5b571c4), unchecked((int) 0x6666ccaa), unchecked((int) 0x484890d8), unchecked((int) 0x03030605), unchecked((int) 0xf6f6f701), + unchecked((int) 0x0e0e1c12), unchecked((int) 0x6161c2a3), unchecked((int) 0x35356a5f), unchecked((int) 0x5757aef9), unchecked((int) 0xb9b969d0), + unchecked((int) 0x86861791), unchecked((int) 0xc1c19958), unchecked((int) 0x1d1d3a27), unchecked((int) 0x9e9e27b9), unchecked((int) 0xe1e1d938), + unchecked((int) 0xf8f8eb13), unchecked((int) 0x98982bb3), unchecked((int) 0x11112233), unchecked((int) 0x6969d2bb), unchecked((int) 0xd9d9a970), + unchecked((int) 0x8e8e0789), unchecked((int) 0x949433a7), unchecked((int) 0x9b9b2db6), unchecked((int) 0x1e1e3c22), unchecked((int) 0x87871592), + unchecked((int) 0xe9e9c920), unchecked((int) 0xcece8749), unchecked((int) 0x5555aaff), unchecked((int) 0x28285078), unchecked((int) 0xdfdfa57a), + unchecked((int) 0x8c8c038f), unchecked((int) 0xa1a159f8), unchecked((int) 0x89890980), unchecked((int) 0x0d0d1a17), unchecked((int) 0xbfbf65da), + unchecked((int) 0xe6e6d731), unchecked((int) 0x424284c6), unchecked((int) 0x6868d0b8), unchecked((int) 0x414182c3), unchecked((int) 0x999929b0), + unchecked((int) 0x2d2d5a77), unchecked((int) 0x0f0f1e11), unchecked((int) 0xb0b07bcb), unchecked((int) 0x5454a8fc), unchecked((int) 0xbbbb6dd6), + unchecked((int) 0x16162c3a)}; + + private static readonly int[] T2 = + { + unchecked((int) 0x63c6a563), unchecked((int) 0x7cf8847c), unchecked((int) 0x77ee9977), unchecked((int) 0x7bf68d7b), unchecked((int) 0xf2ff0df2), + unchecked((int) 0x6bd6bd6b), unchecked((int) 0x6fdeb16f), unchecked((int) 0xc59154c5), unchecked((int) 0x30605030), unchecked((int) 0x01020301), + unchecked((int) 0x67cea967), unchecked((int) 0x2b567d2b), unchecked((int) 0xfee719fe), unchecked((int) 0xd7b562d7), unchecked((int) 0xab4de6ab), + unchecked((int) 0x76ec9a76), unchecked((int) 0xca8f45ca), unchecked((int) 0x821f9d82), unchecked((int) 0xc98940c9), unchecked((int) 0x7dfa877d), + unchecked((int) 0xfaef15fa), unchecked((int) 0x59b2eb59), unchecked((int) 0x478ec947), unchecked((int) 0xf0fb0bf0), unchecked((int) 0xad41ecad), + unchecked((int) 0xd4b367d4), unchecked((int) 0xa25ffda2), unchecked((int) 0xaf45eaaf), unchecked((int) 0x9c23bf9c), unchecked((int) 0xa453f7a4), + unchecked((int) 0x72e49672), unchecked((int) 0xc09b5bc0), unchecked((int) 0xb775c2b7), unchecked((int) 0xfde11cfd), unchecked((int) 0x933dae93), + unchecked((int) 0x264c6a26), unchecked((int) 0x366c5a36), unchecked((int) 0x3f7e413f), unchecked((int) 0xf7f502f7), unchecked((int) 0xcc834fcc), + unchecked((int) 0x34685c34), unchecked((int) 0xa551f4a5), unchecked((int) 0xe5d134e5), unchecked((int) 0xf1f908f1), unchecked((int) 0x71e29371), + unchecked((int) 0xd8ab73d8), unchecked((int) 0x31625331), unchecked((int) 0x152a3f15), unchecked((int) 0x04080c04), unchecked((int) 0xc79552c7), + unchecked((int) 0x23466523), unchecked((int) 0xc39d5ec3), unchecked((int) 0x18302818), unchecked((int) 0x9637a196), unchecked((int) 0x050a0f05), + unchecked((int) 0x9a2fb59a), unchecked((int) 0x070e0907), unchecked((int) 0x12243612), unchecked((int) 0x801b9b80), unchecked((int) 0xe2df3de2), + unchecked((int) 0xebcd26eb), unchecked((int) 0x274e6927), unchecked((int) 0xb27fcdb2), unchecked((int) 0x75ea9f75), unchecked((int) 0x09121b09), + unchecked((int) 0x831d9e83), unchecked((int) 0x2c58742c), unchecked((int) 0x1a342e1a), unchecked((int) 0x1b362d1b), unchecked((int) 0x6edcb26e), + unchecked((int) 0x5ab4ee5a), unchecked((int) 0xa05bfba0), unchecked((int) 0x52a4f652), unchecked((int) 0x3b764d3b), unchecked((int) 0xd6b761d6), + unchecked((int) 0xb37dceb3), unchecked((int) 0x29527b29), unchecked((int) 0xe3dd3ee3), unchecked((int) 0x2f5e712f), unchecked((int) 0x84139784), + unchecked((int) 0x53a6f553), unchecked((int) 0xd1b968d1), unchecked((int) 0x00000000), unchecked((int) 0xedc12ced), unchecked((int) 0x20406020), + unchecked((int) 0xfce31ffc), unchecked((int) 0xb179c8b1), unchecked((int) 0x5bb6ed5b), unchecked((int) 0x6ad4be6a), unchecked((int) 0xcb8d46cb), + unchecked((int) 0xbe67d9be), unchecked((int) 0x39724b39), unchecked((int) 0x4a94de4a), unchecked((int) 0x4c98d44c), unchecked((int) 0x58b0e858), + unchecked((int) 0xcf854acf), unchecked((int) 0xd0bb6bd0), unchecked((int) 0xefc52aef), unchecked((int) 0xaa4fe5aa), unchecked((int) 0xfbed16fb), + unchecked((int) 0x4386c543), unchecked((int) 0x4d9ad74d), unchecked((int) 0x33665533), unchecked((int) 0x85119485), unchecked((int) 0x458acf45), + unchecked((int) 0xf9e910f9), unchecked((int) 0x02040602), unchecked((int) 0x7ffe817f), unchecked((int) 0x50a0f050), unchecked((int) 0x3c78443c), + unchecked((int) 0x9f25ba9f), unchecked((int) 0xa84be3a8), unchecked((int) 0x51a2f351), unchecked((int) 0xa35dfea3), unchecked((int) 0x4080c040), + unchecked((int) 0x8f058a8f), unchecked((int) 0x923fad92), unchecked((int) 0x9d21bc9d), unchecked((int) 0x38704838), unchecked((int) 0xf5f104f5), + unchecked((int) 0xbc63dfbc), unchecked((int) 0xb677c1b6), unchecked((int) 0xdaaf75da), unchecked((int) 0x21426321), unchecked((int) 0x10203010), + unchecked((int) 0xffe51aff), unchecked((int) 0xf3fd0ef3), unchecked((int) 0xd2bf6dd2), unchecked((int) 0xcd814ccd), unchecked((int) 0x0c18140c), + unchecked((int) 0x13263513), unchecked((int) 0xecc32fec), unchecked((int) 0x5fbee15f), unchecked((int) 0x9735a297), unchecked((int) 0x4488cc44), + unchecked((int) 0x172e3917), unchecked((int) 0xc49357c4), unchecked((int) 0xa755f2a7), unchecked((int) 0x7efc827e), unchecked((int) 0x3d7a473d), + unchecked((int) 0x64c8ac64), unchecked((int) 0x5dbae75d), unchecked((int) 0x19322b19), unchecked((int) 0x73e69573), unchecked((int) 0x60c0a060), + unchecked((int) 0x81199881), unchecked((int) 0x4f9ed14f), unchecked((int) 0xdca37fdc), unchecked((int) 0x22446622), unchecked((int) 0x2a547e2a), + unchecked((int) 0x903bab90), unchecked((int) 0x880b8388), unchecked((int) 0x468cca46), unchecked((int) 0xeec729ee), unchecked((int) 0xb86bd3b8), + unchecked((int) 0x14283c14), unchecked((int) 0xdea779de), unchecked((int) 0x5ebce25e), unchecked((int) 0x0b161d0b), unchecked((int) 0xdbad76db), + unchecked((int) 0xe0db3be0), unchecked((int) 0x32645632), unchecked((int) 0x3a744e3a), unchecked((int) 0x0a141e0a), unchecked((int) 0x4992db49), + unchecked((int) 0x060c0a06), unchecked((int) 0x24486c24), unchecked((int) 0x5cb8e45c), unchecked((int) 0xc29f5dc2), unchecked((int) 0xd3bd6ed3), + unchecked((int) 0xac43efac), unchecked((int) 0x62c4a662), unchecked((int) 0x9139a891), unchecked((int) 0x9531a495), unchecked((int) 0xe4d337e4), + unchecked((int) 0x79f28b79), unchecked((int) 0xe7d532e7), unchecked((int) 0xc88b43c8), unchecked((int) 0x376e5937), unchecked((int) 0x6ddab76d), + unchecked((int) 0x8d018c8d), unchecked((int) 0xd5b164d5), unchecked((int) 0x4e9cd24e), unchecked((int) 0xa949e0a9), unchecked((int) 0x6cd8b46c), + unchecked((int) 0x56acfa56), unchecked((int) 0xf4f307f4), unchecked((int) 0xeacf25ea), unchecked((int) 0x65caaf65), unchecked((int) 0x7af48e7a), + unchecked((int) 0xae47e9ae), unchecked((int) 0x08101808), unchecked((int) 0xba6fd5ba), unchecked((int) 0x78f08878), unchecked((int) 0x254a6f25), + unchecked((int) 0x2e5c722e), unchecked((int) 0x1c38241c), unchecked((int) 0xa657f1a6), unchecked((int) 0xb473c7b4), unchecked((int) 0xc69751c6), + unchecked((int) 0xe8cb23e8), unchecked((int) 0xdda17cdd), unchecked((int) 0x74e89c74), unchecked((int) 0x1f3e211f), unchecked((int) 0x4b96dd4b), + unchecked((int) 0xbd61dcbd), unchecked((int) 0x8b0d868b), unchecked((int) 0x8a0f858a), unchecked((int) 0x70e09070), unchecked((int) 0x3e7c423e), + unchecked((int) 0xb571c4b5), unchecked((int) 0x66ccaa66), unchecked((int) 0x4890d848), unchecked((int) 0x03060503), unchecked((int) 0xf6f701f6), + unchecked((int) 0x0e1c120e), unchecked((int) 0x61c2a361), unchecked((int) 0x356a5f35), unchecked((int) 0x57aef957), unchecked((int) 0xb969d0b9), + unchecked((int) 0x86179186), unchecked((int) 0xc19958c1), unchecked((int) 0x1d3a271d), unchecked((int) 0x9e27b99e), unchecked((int) 0xe1d938e1), + unchecked((int) 0xf8eb13f8), unchecked((int) 0x982bb398), unchecked((int) 0x11223311), unchecked((int) 0x69d2bb69), unchecked((int) 0xd9a970d9), + unchecked((int) 0x8e07898e), unchecked((int) 0x9433a794), unchecked((int) 0x9b2db69b), unchecked((int) 0x1e3c221e), unchecked((int) 0x87159287), + unchecked((int) 0xe9c920e9), unchecked((int) 0xce8749ce), unchecked((int) 0x55aaff55), unchecked((int) 0x28507828), unchecked((int) 0xdfa57adf), + unchecked((int) 0x8c038f8c), unchecked((int) 0xa159f8a1), unchecked((int) 0x89098089), unchecked((int) 0x0d1a170d), unchecked((int) 0xbf65dabf), + unchecked((int) 0xe6d731e6), unchecked((int) 0x4284c642), unchecked((int) 0x68d0b868), unchecked((int) 0x4182c341), unchecked((int) 0x9929b099), + unchecked((int) 0x2d5a772d), unchecked((int) 0x0f1e110f), unchecked((int) 0xb07bcbb0), unchecked((int) 0x54a8fc54), unchecked((int) 0xbb6dd6bb), + unchecked((int) 0x162c3a16)}; + + private static readonly int[] T3 = + { + unchecked((int) 0xc6a56363), unchecked((int) 0xf8847c7c), unchecked((int) 0xee997777), unchecked((int) 0xf68d7b7b), unchecked((int) 0xff0df2f2), + unchecked((int) 0xd6bd6b6b), unchecked((int) 0xdeb16f6f), unchecked((int) 0x9154c5c5), unchecked((int) 0x60503030), unchecked((int) 0x02030101), + unchecked((int) 0xcea96767), unchecked((int) 0x567d2b2b), unchecked((int) 0xe719fefe), unchecked((int) 0xb562d7d7), unchecked((int) 0x4de6abab), + unchecked((int) 0xec9a7676), unchecked((int) 0x8f45caca), unchecked((int) 0x1f9d8282), unchecked((int) 0x8940c9c9), unchecked((int) 0xfa877d7d), + unchecked((int) 0xef15fafa), unchecked((int) 0xb2eb5959), unchecked((int) 0x8ec94747), unchecked((int) 0xfb0bf0f0), unchecked((int) 0x41ecadad), + unchecked((int) 0xb367d4d4), unchecked((int) 0x5ffda2a2), unchecked((int) 0x45eaafaf), unchecked((int) 0x23bf9c9c), unchecked((int) 0x53f7a4a4), + unchecked((int) 0xe4967272), unchecked((int) 0x9b5bc0c0), unchecked((int) 0x75c2b7b7), unchecked((int) 0xe11cfdfd), unchecked((int) 0x3dae9393), + unchecked((int) 0x4c6a2626), unchecked((int) 0x6c5a3636), unchecked((int) 0x7e413f3f), unchecked((int) 0xf502f7f7), unchecked((int) 0x834fcccc), + unchecked((int) 0x685c3434), unchecked((int) 0x51f4a5a5), unchecked((int) 0xd134e5e5), unchecked((int) 0xf908f1f1), unchecked((int) 0xe2937171), + unchecked((int) 0xab73d8d8), unchecked((int) 0x62533131), unchecked((int) 0x2a3f1515), unchecked((int) 0x080c0404), unchecked((int) 0x9552c7c7), + unchecked((int) 0x46652323), unchecked((int) 0x9d5ec3c3), unchecked((int) 0x30281818), unchecked((int) 0x37a19696), unchecked((int) 0x0a0f0505), + unchecked((int) 0x2fb59a9a), unchecked((int) 0x0e090707), unchecked((int) 0x24361212), unchecked((int) 0x1b9b8080), unchecked((int) 0xdf3de2e2), + unchecked((int) 0xcd26ebeb), unchecked((int) 0x4e692727), unchecked((int) 0x7fcdb2b2), unchecked((int) 0xea9f7575), unchecked((int) 0x121b0909), + unchecked((int) 0x1d9e8383), unchecked((int) 0x58742c2c), unchecked((int) 0x342e1a1a), unchecked((int) 0x362d1b1b), unchecked((int) 0xdcb26e6e), + unchecked((int) 0xb4ee5a5a), unchecked((int) 0x5bfba0a0), unchecked((int) 0xa4f65252), unchecked((int) 0x764d3b3b), unchecked((int) 0xb761d6d6), + unchecked((int) 0x7dceb3b3), unchecked((int) 0x527b2929), unchecked((int) 0xdd3ee3e3), unchecked((int) 0x5e712f2f), unchecked((int) 0x13978484), + unchecked((int) 0xa6f55353), unchecked((int) 0xb968d1d1), unchecked((int) 0x00000000), unchecked((int) 0xc12ceded), unchecked((int) 0x40602020), + unchecked((int) 0xe31ffcfc), unchecked((int) 0x79c8b1b1), unchecked((int) 0xb6ed5b5b), unchecked((int) 0xd4be6a6a), unchecked((int) 0x8d46cbcb), + unchecked((int) 0x67d9bebe), unchecked((int) 0x724b3939), unchecked((int) 0x94de4a4a), unchecked((int) 0x98d44c4c), unchecked((int) 0xb0e85858), + unchecked((int) 0x854acfcf), unchecked((int) 0xbb6bd0d0), unchecked((int) 0xc52aefef), unchecked((int) 0x4fe5aaaa), unchecked((int) 0xed16fbfb), + unchecked((int) 0x86c54343), unchecked((int) 0x9ad74d4d), unchecked((int) 0x66553333), unchecked((int) 0x11948585), unchecked((int) 0x8acf4545), + unchecked((int) 0xe910f9f9), unchecked((int) 0x04060202), unchecked((int) 0xfe817f7f), unchecked((int) 0xa0f05050), unchecked((int) 0x78443c3c), + unchecked((int) 0x25ba9f9f), unchecked((int) 0x4be3a8a8), unchecked((int) 0xa2f35151), unchecked((int) 0x5dfea3a3), unchecked((int) 0x80c04040), + unchecked((int) 0x058a8f8f), unchecked((int) 0x3fad9292), unchecked((int) 0x21bc9d9d), unchecked((int) 0x70483838), unchecked((int) 0xf104f5f5), + unchecked((int) 0x63dfbcbc), unchecked((int) 0x77c1b6b6), unchecked((int) 0xaf75dada), unchecked((int) 0x42632121), unchecked((int) 0x20301010), + unchecked((int) 0xe51affff), unchecked((int) 0xfd0ef3f3), unchecked((int) 0xbf6dd2d2), unchecked((int) 0x814ccdcd), unchecked((int) 0x18140c0c), + unchecked((int) 0x26351313), unchecked((int) 0xc32fecec), unchecked((int) 0xbee15f5f), unchecked((int) 0x35a29797), unchecked((int) 0x88cc4444), + unchecked((int) 0x2e391717), unchecked((int) 0x9357c4c4), unchecked((int) 0x55f2a7a7), unchecked((int) 0xfc827e7e), unchecked((int) 0x7a473d3d), + unchecked((int) 0xc8ac6464), unchecked((int) 0xbae75d5d), unchecked((int) 0x322b1919), unchecked((int) 0xe6957373), unchecked((int) 0xc0a06060), + unchecked((int) 0x19988181), unchecked((int) 0x9ed14f4f), unchecked((int) 0xa37fdcdc), unchecked((int) 0x44662222), unchecked((int) 0x547e2a2a), + unchecked((int) 0x3bab9090), unchecked((int) 0x0b838888), unchecked((int) 0x8cca4646), unchecked((int) 0xc729eeee), unchecked((int) 0x6bd3b8b8), + unchecked((int) 0x283c1414), unchecked((int) 0xa779dede), unchecked((int) 0xbce25e5e), unchecked((int) 0x161d0b0b), unchecked((int) 0xad76dbdb), + unchecked((int) 0xdb3be0e0), unchecked((int) 0x64563232), unchecked((int) 0x744e3a3a), unchecked((int) 0x141e0a0a), unchecked((int) 0x92db4949), + unchecked((int) 0x0c0a0606), unchecked((int) 0x486c2424), unchecked((int) 0xb8e45c5c), unchecked((int) 0x9f5dc2c2), unchecked((int) 0xbd6ed3d3), + unchecked((int) 0x43efacac), unchecked((int) 0xc4a66262), unchecked((int) 0x39a89191), unchecked((int) 0x31a49595), unchecked((int) 0xd337e4e4), + unchecked((int) 0xf28b7979), unchecked((int) 0xd532e7e7), unchecked((int) 0x8b43c8c8), unchecked((int) 0x6e593737), unchecked((int) 0xdab76d6d), + unchecked((int) 0x018c8d8d), unchecked((int) 0xb164d5d5), unchecked((int) 0x9cd24e4e), unchecked((int) 0x49e0a9a9), unchecked((int) 0xd8b46c6c), + unchecked((int) 0xacfa5656), unchecked((int) 0xf307f4f4), unchecked((int) 0xcf25eaea), unchecked((int) 0xcaaf6565), unchecked((int) 0xf48e7a7a), + unchecked((int) 0x47e9aeae), unchecked((int) 0x10180808), unchecked((int) 0x6fd5baba), unchecked((int) 0xf0887878), unchecked((int) 0x4a6f2525), + unchecked((int) 0x5c722e2e), unchecked((int) 0x38241c1c), unchecked((int) 0x57f1a6a6), unchecked((int) 0x73c7b4b4), unchecked((int) 0x9751c6c6), + unchecked((int) 0xcb23e8e8), unchecked((int) 0xa17cdddd), unchecked((int) 0xe89c7474), unchecked((int) 0x3e211f1f), unchecked((int) 0x96dd4b4b), + unchecked((int) 0x61dcbdbd), unchecked((int) 0x0d868b8b), unchecked((int) 0x0f858a8a), unchecked((int) 0xe0907070), unchecked((int) 0x7c423e3e), + unchecked((int) 0x71c4b5b5), unchecked((int) 0xccaa6666), unchecked((int) 0x90d84848), unchecked((int) 0x06050303), unchecked((int) 0xf701f6f6), + unchecked((int) 0x1c120e0e), unchecked((int) 0xc2a36161), unchecked((int) 0x6a5f3535), unchecked((int) 0xaef95757), unchecked((int) 0x69d0b9b9), + unchecked((int) 0x17918686), unchecked((int) 0x9958c1c1), unchecked((int) 0x3a271d1d), unchecked((int) 0x27b99e9e), unchecked((int) 0xd938e1e1), + unchecked((int) 0xeb13f8f8), unchecked((int) 0x2bb39898), unchecked((int) 0x22331111), unchecked((int) 0xd2bb6969), unchecked((int) 0xa970d9d9), + unchecked((int) 0x07898e8e), unchecked((int) 0x33a79494), unchecked((int) 0x2db69b9b), unchecked((int) 0x3c221e1e), unchecked((int) 0x15928787), + unchecked((int) 0xc920e9e9), unchecked((int) 0x8749cece), unchecked((int) 0xaaff5555), unchecked((int) 0x50782828), unchecked((int) 0xa57adfdf), + unchecked((int) 0x038f8c8c), unchecked((int) 0x59f8a1a1), unchecked((int) 0x09808989), unchecked((int) 0x1a170d0d), unchecked((int) 0x65dabfbf), + unchecked((int) 0xd731e6e6), unchecked((int) 0x84c64242), unchecked((int) 0xd0b86868), unchecked((int) 0x82c34141), unchecked((int) 0x29b09999), + unchecked((int) 0x5a772d2d), unchecked((int) 0x1e110f0f), unchecked((int) 0x7bcbb0b0), unchecked((int) 0xa8fc5454), unchecked((int) 0x6dd6bbbb), + unchecked((int) 0x2c3a1616)}; + + private static readonly int[] Tinv0 = + { + unchecked((int) 0x50a7f451), unchecked((int) 0x5365417e), unchecked((int) 0xc3a4171a), unchecked((int) 0x965e273a), unchecked((int) 0xcb6bab3b), + unchecked((int) 0xf1459d1f), unchecked((int) 0xab58faac), unchecked((int) 0x9303e34b), unchecked((int) 0x55fa3020), unchecked((int) 0xf66d76ad), + unchecked((int) 0x9176cc88), unchecked((int) 0x254c02f5), unchecked((int) 0xfcd7e54f), unchecked((int) 0xd7cb2ac5), unchecked((int) 0x80443526), + unchecked((int) 0x8fa362b5), unchecked((int) 0x495ab1de), unchecked((int) 0x671bba25), unchecked((int) 0x980eea45), unchecked((int) 0xe1c0fe5d), + unchecked((int) 0x02752fc3), unchecked((int) 0x12f04c81), unchecked((int) 0xa397468d), unchecked((int) 0xc6f9d36b), unchecked((int) 0xe75f8f03), + unchecked((int) 0x959c9215), unchecked((int) 0xeb7a6dbf), unchecked((int) 0xda595295), unchecked((int) 0x2d83bed4), unchecked((int) 0xd3217458), + unchecked((int) 0x2969e049), unchecked((int) 0x44c8c98e), unchecked((int) 0x6a89c275), unchecked((int) 0x78798ef4), unchecked((int) 0x6b3e5899), + unchecked((int) 0xdd71b927), unchecked((int) 0xb64fe1be), unchecked((int) 0x17ad88f0), unchecked((int) 0x66ac20c9), unchecked((int) 0xb43ace7d), + unchecked((int) 0x184adf63), unchecked((int) 0x82311ae5), unchecked((int) 0x60335197), unchecked((int) 0x457f5362), unchecked((int) 0xe07764b1), + unchecked((int) 0x84ae6bbb), unchecked((int) 0x1ca081fe), unchecked((int) 0x942b08f9), unchecked((int) 0x58684870), unchecked((int) 0x19fd458f), + unchecked((int) 0x876cde94), unchecked((int) 0xb7f87b52), unchecked((int) 0x23d373ab), unchecked((int) 0xe2024b72), unchecked((int) 0x578f1fe3), + unchecked((int) 0x2aab5566), unchecked((int) 0x0728ebb2), unchecked((int) 0x03c2b52f), unchecked((int) 0x9a7bc586), unchecked((int) 0xa50837d3), + unchecked((int) 0xf2872830), unchecked((int) 0xb2a5bf23), unchecked((int) 0xba6a0302), unchecked((int) 0x5c8216ed), unchecked((int) 0x2b1ccf8a), + unchecked((int) 0x92b479a7), unchecked((int) 0xf0f207f3), unchecked((int) 0xa1e2694e), unchecked((int) 0xcdf4da65), unchecked((int) 0xd5be0506), + unchecked((int) 0x1f6234d1), unchecked((int) 0x8afea6c4), unchecked((int) 0x9d532e34), unchecked((int) 0xa055f3a2), unchecked((int) 0x32e18a05), + unchecked((int) 0x75ebf6a4), unchecked((int) 0x39ec830b), unchecked((int) 0xaaef6040), unchecked((int) 0x069f715e), unchecked((int) 0x51106ebd), + unchecked((int) 0xf98a213e), unchecked((int) 0x3d06dd96), unchecked((int) 0xae053edd), unchecked((int) 0x46bde64d), unchecked((int) 0xb58d5491), + unchecked((int) 0x055dc471), unchecked((int) 0x6fd40604), unchecked((int) 0xff155060), unchecked((int) 0x24fb9819), unchecked((int) 0x97e9bdd6), + unchecked((int) 0xcc434089), unchecked((int) 0x779ed967), unchecked((int) 0xbd42e8b0), unchecked((int) 0x888b8907), unchecked((int) 0x385b19e7), + unchecked((int) 0xdbeec879), unchecked((int) 0x470a7ca1), unchecked((int) 0xe90f427c), unchecked((int) 0xc91e84f8), unchecked((int) 0x00000000), + unchecked((int) 0x83868009), unchecked((int) 0x48ed2b32), unchecked((int) 0xac70111e), unchecked((int) 0x4e725a6c), unchecked((int) 0xfbff0efd), + unchecked((int) 0x5638850f), unchecked((int) 0x1ed5ae3d), unchecked((int) 0x27392d36), unchecked((int) 0x64d90f0a), unchecked((int) 0x21a65c68), + unchecked((int) 0xd1545b9b), unchecked((int) 0x3a2e3624), unchecked((int) 0xb1670a0c), unchecked((int) 0x0fe75793), unchecked((int) 0xd296eeb4), + unchecked((int) 0x9e919b1b), unchecked((int) 0x4fc5c080), unchecked((int) 0xa220dc61), unchecked((int) 0x694b775a), unchecked((int) 0x161a121c), + unchecked((int) 0x0aba93e2), unchecked((int) 0xe52aa0c0), unchecked((int) 0x43e0223c), unchecked((int) 0x1d171b12), unchecked((int) 0x0b0d090e), + unchecked((int) 0xadc78bf2), unchecked((int) 0xb9a8b62d), unchecked((int) 0xc8a91e14), unchecked((int) 0x8519f157), unchecked((int) 0x4c0775af), + unchecked((int) 0xbbdd99ee), unchecked((int) 0xfd607fa3), unchecked((int) 0x9f2601f7), unchecked((int) 0xbcf5725c), unchecked((int) 0xc53b6644), + unchecked((int) 0x347efb5b), unchecked((int) 0x7629438b), unchecked((int) 0xdcc623cb), unchecked((int) 0x68fcedb6), unchecked((int) 0x63f1e4b8), + unchecked((int) 0xcadc31d7), unchecked((int) 0x10856342), unchecked((int) 0x40229713), unchecked((int) 0x2011c684), unchecked((int) 0x7d244a85), + unchecked((int) 0xf83dbbd2), unchecked((int) 0x1132f9ae), unchecked((int) 0x6da129c7), unchecked((int) 0x4b2f9e1d), unchecked((int) 0xf330b2dc), + unchecked((int) 0xec52860d), unchecked((int) 0xd0e3c177), unchecked((int) 0x6c16b32b), unchecked((int) 0x99b970a9), unchecked((int) 0xfa489411), + unchecked((int) 0x2264e947), unchecked((int) 0xc48cfca8), unchecked((int) 0x1a3ff0a0), unchecked((int) 0xd82c7d56), unchecked((int) 0xef903322), + unchecked((int) 0xc74e4987), unchecked((int) 0xc1d138d9), unchecked((int) 0xfea2ca8c), unchecked((int) 0x360bd498), unchecked((int) 0xcf81f5a6), + unchecked((int) 0x28de7aa5), unchecked((int) 0x268eb7da), unchecked((int) 0xa4bfad3f), unchecked((int) 0xe49d3a2c), unchecked((int) 0x0d927850), + unchecked((int) 0x9bcc5f6a), unchecked((int) 0x62467e54), unchecked((int) 0xc2138df6), unchecked((int) 0xe8b8d890), unchecked((int) 0x5ef7392e), + unchecked((int) 0xf5afc382), unchecked((int) 0xbe805d9f), unchecked((int) 0x7c93d069), unchecked((int) 0xa92dd56f), unchecked((int) 0xb31225cf), + unchecked((int) 0x3b99acc8), unchecked((int) 0xa77d1810), unchecked((int) 0x6e639ce8), unchecked((int) 0x7bbb3bdb), unchecked((int) 0x097826cd), + unchecked((int) 0xf418596e), unchecked((int) 0x01b79aec), unchecked((int) 0xa89a4f83), unchecked((int) 0x656e95e6), unchecked((int) 0x7ee6ffaa), + unchecked((int) 0x08cfbc21), unchecked((int) 0xe6e815ef), unchecked((int) 0xd99be7ba), unchecked((int) 0xce366f4a), unchecked((int) 0xd4099fea), + unchecked((int) 0xd67cb029), unchecked((int) 0xafb2a431), unchecked((int) 0x31233f2a), unchecked((int) 0x3094a5c6), unchecked((int) 0xc066a235), + unchecked((int) 0x37bc4e74), unchecked((int) 0xa6ca82fc), unchecked((int) 0xb0d090e0), unchecked((int) 0x15d8a733), unchecked((int) 0x4a9804f1), + unchecked((int) 0xf7daec41), unchecked((int) 0x0e50cd7f), unchecked((int) 0x2ff69117), unchecked((int) 0x8dd64d76), unchecked((int) 0x4db0ef43), + unchecked((int) 0x544daacc), unchecked((int) 0xdf0496e4), unchecked((int) 0xe3b5d19e), unchecked((int) 0x1b886a4c), unchecked((int) 0xb81f2cc1), + unchecked((int) 0x7f516546), unchecked((int) 0x04ea5e9d), unchecked((int) 0x5d358c01), unchecked((int) 0x737487fa), unchecked((int) 0x2e410bfb), + unchecked((int) 0x5a1d67b3), unchecked((int) 0x52d2db92), unchecked((int) 0x335610e9), unchecked((int) 0x1347d66d), unchecked((int) 0x8c61d79a), + unchecked((int) 0x7a0ca137), unchecked((int) 0x8e14f859), unchecked((int) 0x893c13eb), unchecked((int) 0xee27a9ce), unchecked((int) 0x35c961b7), + unchecked((int) 0xede51ce1), unchecked((int) 0x3cb1477a), unchecked((int) 0x59dfd29c), unchecked((int) 0x3f73f255), unchecked((int) 0x79ce1418), + unchecked((int) 0xbf37c773), unchecked((int) 0xeacdf753), unchecked((int) 0x5baafd5f), unchecked((int) 0x146f3ddf), unchecked((int) 0x86db4478), + unchecked((int) 0x81f3afca), unchecked((int) 0x3ec468b9), unchecked((int) 0x2c342438), unchecked((int) 0x5f40a3c2), unchecked((int) 0x72c31d16), + unchecked((int) 0x0c25e2bc), unchecked((int) 0x8b493c28), unchecked((int) 0x41950dff), unchecked((int) 0x7101a839), unchecked((int) 0xdeb30c08), + unchecked((int) 0x9ce4b4d8), unchecked((int) 0x90c15664), unchecked((int) 0x6184cb7b), unchecked((int) 0x70b632d5), unchecked((int) 0x745c6c48), + unchecked((int) 0x4257b8d0)}; + + private static readonly int[] Tinv1 = + { + unchecked((int) 0xa7f45150), unchecked((int) 0x65417e53), unchecked((int) 0xa4171ac3), unchecked((int) 0x5e273a96), unchecked((int) 0x6bab3bcb), + unchecked((int) 0x459d1ff1), unchecked((int) 0x58faacab), unchecked((int) 0x03e34b93), unchecked((int) 0xfa302055), unchecked((int) 0x6d76adf6), + unchecked((int) 0x76cc8891), unchecked((int) 0x4c02f525), unchecked((int) 0xd7e54ffc), unchecked((int) 0xcb2ac5d7), unchecked((int) 0x44352680), + unchecked((int) 0xa362b58f), unchecked((int) 0x5ab1de49), unchecked((int) 0x1bba2567), unchecked((int) 0x0eea4598), unchecked((int) 0xc0fe5de1), + unchecked((int) 0x752fc302), unchecked((int) 0xf04c8112), unchecked((int) 0x97468da3), unchecked((int) 0xf9d36bc6), unchecked((int) 0x5f8f03e7), + unchecked((int) 0x9c921595), unchecked((int) 0x7a6dbfeb), unchecked((int) 0x595295da), unchecked((int) 0x83bed42d), unchecked((int) 0x217458d3), + unchecked((int) 0x69e04929), unchecked((int) 0xc8c98e44), unchecked((int) 0x89c2756a), unchecked((int) 0x798ef478), unchecked((int) 0x3e58996b), + unchecked((int) 0x71b927dd), unchecked((int) 0x4fe1beb6), unchecked((int) 0xad88f017), unchecked((int) 0xac20c966), unchecked((int) 0x3ace7db4), + unchecked((int) 0x4adf6318), unchecked((int) 0x311ae582), unchecked((int) 0x33519760), unchecked((int) 0x7f536245), unchecked((int) 0x7764b1e0), + unchecked((int) 0xae6bbb84), unchecked((int) 0xa081fe1c), unchecked((int) 0x2b08f994), unchecked((int) 0x68487058), unchecked((int) 0xfd458f19), + unchecked((int) 0x6cde9487), unchecked((int) 0xf87b52b7), unchecked((int) 0xd373ab23), unchecked((int) 0x024b72e2), unchecked((int) 0x8f1fe357), + unchecked((int) 0xab55662a), unchecked((int) 0x28ebb207), unchecked((int) 0xc2b52f03), unchecked((int) 0x7bc5869a), unchecked((int) 0x0837d3a5), + unchecked((int) 0x872830f2), unchecked((int) 0xa5bf23b2), unchecked((int) 0x6a0302ba), unchecked((int) 0x8216ed5c), unchecked((int) 0x1ccf8a2b), + unchecked((int) 0xb479a792), unchecked((int) 0xf207f3f0), unchecked((int) 0xe2694ea1), unchecked((int) 0xf4da65cd), unchecked((int) 0xbe0506d5), + unchecked((int) 0x6234d11f), unchecked((int) 0xfea6c48a), unchecked((int) 0x532e349d), unchecked((int) 0x55f3a2a0), unchecked((int) 0xe18a0532), + unchecked((int) 0xebf6a475), unchecked((int) 0xec830b39), unchecked((int) 0xef6040aa), unchecked((int) 0x9f715e06), unchecked((int) 0x106ebd51), + unchecked((int) 0x8a213ef9), unchecked((int) 0x06dd963d), unchecked((int) 0x053eddae), unchecked((int) 0xbde64d46), unchecked((int) 0x8d5491b5), + unchecked((int) 0x5dc47105), unchecked((int) 0xd406046f), unchecked((int) 0x155060ff), unchecked((int) 0xfb981924), unchecked((int) 0xe9bdd697), + unchecked((int) 0x434089cc), unchecked((int) 0x9ed96777), unchecked((int) 0x42e8b0bd), unchecked((int) 0x8b890788), unchecked((int) 0x5b19e738), + unchecked((int) 0xeec879db), unchecked((int) 0x0a7ca147), unchecked((int) 0x0f427ce9), unchecked((int) 0x1e84f8c9), unchecked((int) 0x00000000), + unchecked((int) 0x86800983), unchecked((int) 0xed2b3248), unchecked((int) 0x70111eac), unchecked((int) 0x725a6c4e), unchecked((int) 0xff0efdfb), + unchecked((int) 0x38850f56), unchecked((int) 0xd5ae3d1e), unchecked((int) 0x392d3627), unchecked((int) 0xd90f0a64), unchecked((int) 0xa65c6821), + unchecked((int) 0x545b9bd1), unchecked((int) 0x2e36243a), unchecked((int) 0x670a0cb1), unchecked((int) 0xe757930f), unchecked((int) 0x96eeb4d2), + unchecked((int) 0x919b1b9e), unchecked((int) 0xc5c0804f), unchecked((int) 0x20dc61a2), unchecked((int) 0x4b775a69), unchecked((int) 0x1a121c16), + unchecked((int) 0xba93e20a), unchecked((int) 0x2aa0c0e5), unchecked((int) 0xe0223c43), unchecked((int) 0x171b121d), unchecked((int) 0x0d090e0b), + unchecked((int) 0xc78bf2ad), unchecked((int) 0xa8b62db9), unchecked((int) 0xa91e14c8), unchecked((int) 0x19f15785), unchecked((int) 0x0775af4c), + unchecked((int) 0xdd99eebb), unchecked((int) 0x607fa3fd), unchecked((int) 0x2601f79f), unchecked((int) 0xf5725cbc), unchecked((int) 0x3b6644c5), + unchecked((int) 0x7efb5b34), unchecked((int) 0x29438b76), unchecked((int) 0xc623cbdc), unchecked((int) 0xfcedb668), unchecked((int) 0xf1e4b863), + unchecked((int) 0xdc31d7ca), unchecked((int) 0x85634210), unchecked((int) 0x22971340), unchecked((int) 0x11c68420), unchecked((int) 0x244a857d), + unchecked((int) 0x3dbbd2f8), unchecked((int) 0x32f9ae11), unchecked((int) 0xa129c76d), unchecked((int) 0x2f9e1d4b), unchecked((int) 0x30b2dcf3), + unchecked((int) 0x52860dec), unchecked((int) 0xe3c177d0), unchecked((int) 0x16b32b6c), unchecked((int) 0xb970a999), unchecked((int) 0x489411fa), + unchecked((int) 0x64e94722), unchecked((int) 0x8cfca8c4), unchecked((int) 0x3ff0a01a), unchecked((int) 0x2c7d56d8), unchecked((int) 0x903322ef), + unchecked((int) 0x4e4987c7), unchecked((int) 0xd138d9c1), unchecked((int) 0xa2ca8cfe), unchecked((int) 0x0bd49836), unchecked((int) 0x81f5a6cf), + unchecked((int) 0xde7aa528), unchecked((int) 0x8eb7da26), unchecked((int) 0xbfad3fa4), unchecked((int) 0x9d3a2ce4), unchecked((int) 0x9278500d), + unchecked((int) 0xcc5f6a9b), unchecked((int) 0x467e5462), unchecked((int) 0x138df6c2), unchecked((int) 0xb8d890e8), unchecked((int) 0xf7392e5e), + unchecked((int) 0xafc382f5), unchecked((int) 0x805d9fbe), unchecked((int) 0x93d0697c), unchecked((int) 0x2dd56fa9), unchecked((int) 0x1225cfb3), + unchecked((int) 0x99acc83b), unchecked((int) 0x7d1810a7), unchecked((int) 0x639ce86e), unchecked((int) 0xbb3bdb7b), unchecked((int) 0x7826cd09), + unchecked((int) 0x18596ef4), unchecked((int) 0xb79aec01), unchecked((int) 0x9a4f83a8), unchecked((int) 0x6e95e665), unchecked((int) 0xe6ffaa7e), + unchecked((int) 0xcfbc2108), unchecked((int) 0xe815efe6), unchecked((int) 0x9be7bad9), unchecked((int) 0x366f4ace), unchecked((int) 0x099fead4), + unchecked((int) 0x7cb029d6), unchecked((int) 0xb2a431af), unchecked((int) 0x233f2a31), unchecked((int) 0x94a5c630), unchecked((int) 0x66a235c0), + unchecked((int) 0xbc4e7437), unchecked((int) 0xca82fca6), unchecked((int) 0xd090e0b0), unchecked((int) 0xd8a73315), unchecked((int) 0x9804f14a), + unchecked((int) 0xdaec41f7), unchecked((int) 0x50cd7f0e), unchecked((int) 0xf691172f), unchecked((int) 0xd64d768d), unchecked((int) 0xb0ef434d), + unchecked((int) 0x4daacc54), unchecked((int) 0x0496e4df), unchecked((int) 0xb5d19ee3), unchecked((int) 0x886a4c1b), unchecked((int) 0x1f2cc1b8), + unchecked((int) 0x5165467f), unchecked((int) 0xea5e9d04), unchecked((int) 0x358c015d), unchecked((int) 0x7487fa73), unchecked((int) 0x410bfb2e), + unchecked((int) 0x1d67b35a), unchecked((int) 0xd2db9252), unchecked((int) 0x5610e933), unchecked((int) 0x47d66d13), unchecked((int) 0x61d79a8c), + unchecked((int) 0x0ca1377a), unchecked((int) 0x14f8598e), unchecked((int) 0x3c13eb89), unchecked((int) 0x27a9ceee), unchecked((int) 0xc961b735), + unchecked((int) 0xe51ce1ed), unchecked((int) 0xb1477a3c), unchecked((int) 0xdfd29c59), unchecked((int) 0x73f2553f), unchecked((int) 0xce141879), + unchecked((int) 0x37c773bf), unchecked((int) 0xcdf753ea), unchecked((int) 0xaafd5f5b), unchecked((int) 0x6f3ddf14), unchecked((int) 0xdb447886), + unchecked((int) 0xf3afca81), unchecked((int) 0xc468b93e), unchecked((int) 0x3424382c), unchecked((int) 0x40a3c25f), unchecked((int) 0xc31d1672), + unchecked((int) 0x25e2bc0c), unchecked((int) 0x493c288b), unchecked((int) 0x950dff41), unchecked((int) 0x01a83971), unchecked((int) 0xb30c08de), + unchecked((int) 0xe4b4d89c), unchecked((int) 0xc1566490), unchecked((int) 0x84cb7b61), unchecked((int) 0xb632d570), unchecked((int) 0x5c6c4874), + unchecked((int) 0x57b8d042)}; + + private static readonly int[] Tinv2 = + { + unchecked((int) 0xf45150a7), unchecked((int) 0x417e5365), unchecked((int) 0x171ac3a4), unchecked((int) 0x273a965e), unchecked((int) 0xab3bcb6b), + unchecked((int) 0x9d1ff145), unchecked((int) 0xfaacab58), unchecked((int) 0xe34b9303), unchecked((int) 0x302055fa), unchecked((int) 0x76adf66d), + unchecked((int) 0xcc889176), unchecked((int) 0x02f5254c), unchecked((int) 0xe54ffcd7), unchecked((int) 0x2ac5d7cb), unchecked((int) 0x35268044), + unchecked((int) 0x62b58fa3), unchecked((int) 0xb1de495a), unchecked((int) 0xba25671b), unchecked((int) 0xea45980e), unchecked((int) 0xfe5de1c0), + unchecked((int) 0x2fc30275), unchecked((int) 0x4c8112f0), unchecked((int) 0x468da397), unchecked((int) 0xd36bc6f9), unchecked((int) 0x8f03e75f), + unchecked((int) 0x9215959c), unchecked((int) 0x6dbfeb7a), unchecked((int) 0x5295da59), unchecked((int) 0xbed42d83), unchecked((int) 0x7458d321), + unchecked((int) 0xe0492969), unchecked((int) 0xc98e44c8), unchecked((int) 0xc2756a89), unchecked((int) 0x8ef47879), unchecked((int) 0x58996b3e), + unchecked((int) 0xb927dd71), unchecked((int) 0xe1beb64f), unchecked((int) 0x88f017ad), unchecked((int) 0x20c966ac), unchecked((int) 0xce7db43a), + unchecked((int) 0xdf63184a), unchecked((int) 0x1ae58231), unchecked((int) 0x51976033), unchecked((int) 0x5362457f), unchecked((int) 0x64b1e077), + unchecked((int) 0x6bbb84ae), unchecked((int) 0x81fe1ca0), unchecked((int) 0x08f9942b), unchecked((int) 0x48705868), unchecked((int) 0x458f19fd), + unchecked((int) 0xde94876c), unchecked((int) 0x7b52b7f8), unchecked((int) 0x73ab23d3), unchecked((int) 0x4b72e202), unchecked((int) 0x1fe3578f), + unchecked((int) 0x55662aab), unchecked((int) 0xebb20728), unchecked((int) 0xb52f03c2), unchecked((int) 0xc5869a7b), unchecked((int) 0x37d3a508), + unchecked((int) 0x2830f287), unchecked((int) 0xbf23b2a5), unchecked((int) 0x0302ba6a), unchecked((int) 0x16ed5c82), unchecked((int) 0xcf8a2b1c), + unchecked((int) 0x79a792b4), unchecked((int) 0x07f3f0f2), unchecked((int) 0x694ea1e2), unchecked((int) 0xda65cdf4), unchecked((int) 0x0506d5be), + unchecked((int) 0x34d11f62), unchecked((int) 0xa6c48afe), unchecked((int) 0x2e349d53), unchecked((int) 0xf3a2a055), unchecked((int) 0x8a0532e1), + unchecked((int) 0xf6a475eb), unchecked((int) 0x830b39ec), unchecked((int) 0x6040aaef), unchecked((int) 0x715e069f), unchecked((int) 0x6ebd5110), + unchecked((int) 0x213ef98a), unchecked((int) 0xdd963d06), unchecked((int) 0x3eddae05), unchecked((int) 0xe64d46bd), unchecked((int) 0x5491b58d), + unchecked((int) 0xc471055d), unchecked((int) 0x06046fd4), unchecked((int) 0x5060ff15), unchecked((int) 0x981924fb), unchecked((int) 0xbdd697e9), + unchecked((int) 0x4089cc43), unchecked((int) 0xd967779e), unchecked((int) 0xe8b0bd42), unchecked((int) 0x8907888b), unchecked((int) 0x19e7385b), + unchecked((int) 0xc879dbee), unchecked((int) 0x7ca1470a), unchecked((int) 0x427ce90f), unchecked((int) 0x84f8c91e), unchecked((int) 0x00000000), + unchecked((int) 0x80098386), unchecked((int) 0x2b3248ed), unchecked((int) 0x111eac70), unchecked((int) 0x5a6c4e72), unchecked((int) 0x0efdfbff), + unchecked((int) 0x850f5638), unchecked((int) 0xae3d1ed5), unchecked((int) 0x2d362739), unchecked((int) 0x0f0a64d9), unchecked((int) 0x5c6821a6), + unchecked((int) 0x5b9bd154), unchecked((int) 0x36243a2e), unchecked((int) 0x0a0cb167), unchecked((int) 0x57930fe7), unchecked((int) 0xeeb4d296), + unchecked((int) 0x9b1b9e91), unchecked((int) 0xc0804fc5), unchecked((int) 0xdc61a220), unchecked((int) 0x775a694b), unchecked((int) 0x121c161a), + unchecked((int) 0x93e20aba), unchecked((int) 0xa0c0e52a), unchecked((int) 0x223c43e0), unchecked((int) 0x1b121d17), unchecked((int) 0x090e0b0d), + unchecked((int) 0x8bf2adc7), unchecked((int) 0xb62db9a8), unchecked((int) 0x1e14c8a9), unchecked((int) 0xf1578519), unchecked((int) 0x75af4c07), + unchecked((int) 0x99eebbdd), unchecked((int) 0x7fa3fd60), unchecked((int) 0x01f79f26), unchecked((int) 0x725cbcf5), unchecked((int) 0x6644c53b), + unchecked((int) 0xfb5b347e), unchecked((int) 0x438b7629), unchecked((int) 0x23cbdcc6), unchecked((int) 0xedb668fc), unchecked((int) 0xe4b863f1), + unchecked((int) 0x31d7cadc), unchecked((int) 0x63421085), unchecked((int) 0x97134022), unchecked((int) 0xc6842011), unchecked((int) 0x4a857d24), + unchecked((int) 0xbbd2f83d), unchecked((int) 0xf9ae1132), unchecked((int) 0x29c76da1), unchecked((int) 0x9e1d4b2f), unchecked((int) 0xb2dcf330), + unchecked((int) 0x860dec52), unchecked((int) 0xc177d0e3), unchecked((int) 0xb32b6c16), unchecked((int) 0x70a999b9), unchecked((int) 0x9411fa48), + unchecked((int) 0xe9472264), unchecked((int) 0xfca8c48c), unchecked((int) 0xf0a01a3f), unchecked((int) 0x7d56d82c), unchecked((int) 0x3322ef90), + unchecked((int) 0x4987c74e), unchecked((int) 0x38d9c1d1), unchecked((int) 0xca8cfea2), unchecked((int) 0xd498360b), unchecked((int) 0xf5a6cf81), + unchecked((int) 0x7aa528de), unchecked((int) 0xb7da268e), unchecked((int) 0xad3fa4bf), unchecked((int) 0x3a2ce49d), unchecked((int) 0x78500d92), + unchecked((int) 0x5f6a9bcc), unchecked((int) 0x7e546246), unchecked((int) 0x8df6c213), unchecked((int) 0xd890e8b8), unchecked((int) 0x392e5ef7), + unchecked((int) 0xc382f5af), unchecked((int) 0x5d9fbe80), unchecked((int) 0xd0697c93), unchecked((int) 0xd56fa92d), unchecked((int) 0x25cfb312), + unchecked((int) 0xacc83b99), unchecked((int) 0x1810a77d), unchecked((int) 0x9ce86e63), unchecked((int) 0x3bdb7bbb), unchecked((int) 0x26cd0978), + unchecked((int) 0x596ef418), unchecked((int) 0x9aec01b7), unchecked((int) 0x4f83a89a), unchecked((int) 0x95e6656e), unchecked((int) 0xffaa7ee6), + unchecked((int) 0xbc2108cf), unchecked((int) 0x15efe6e8), unchecked((int) 0xe7bad99b), unchecked((int) 0x6f4ace36), unchecked((int) 0x9fead409), + unchecked((int) 0xb029d67c), unchecked((int) 0xa431afb2), unchecked((int) 0x3f2a3123), unchecked((int) 0xa5c63094), unchecked((int) 0xa235c066), + unchecked((int) 0x4e7437bc), unchecked((int) 0x82fca6ca), unchecked((int) 0x90e0b0d0), unchecked((int) 0xa73315d8), unchecked((int) 0x04f14a98), + unchecked((int) 0xec41f7da), unchecked((int) 0xcd7f0e50), unchecked((int) 0x91172ff6), unchecked((int) 0x4d768dd6), unchecked((int) 0xef434db0), + unchecked((int) 0xaacc544d), unchecked((int) 0x96e4df04), unchecked((int) 0xd19ee3b5), unchecked((int) 0x6a4c1b88), unchecked((int) 0x2cc1b81f), + unchecked((int) 0x65467f51), unchecked((int) 0x5e9d04ea), unchecked((int) 0x8c015d35), unchecked((int) 0x87fa7374), unchecked((int) 0x0bfb2e41), + unchecked((int) 0x67b35a1d), unchecked((int) 0xdb9252d2), unchecked((int) 0x10e93356), unchecked((int) 0xd66d1347), unchecked((int) 0xd79a8c61), + unchecked((int) 0xa1377a0c), unchecked((int) 0xf8598e14), unchecked((int) 0x13eb893c), unchecked((int) 0xa9ceee27), unchecked((int) 0x61b735c9), + unchecked((int) 0x1ce1ede5), unchecked((int) 0x477a3cb1), unchecked((int) 0xd29c59df), unchecked((int) 0xf2553f73), unchecked((int) 0x141879ce), + unchecked((int) 0xc773bf37), unchecked((int) 0xf753eacd), unchecked((int) 0xfd5f5baa), unchecked((int) 0x3ddf146f), unchecked((int) 0x447886db), + unchecked((int) 0xafca81f3), unchecked((int) 0x68b93ec4), unchecked((int) 0x24382c34), unchecked((int) 0xa3c25f40), unchecked((int) 0x1d1672c3), + unchecked((int) 0xe2bc0c25), unchecked((int) 0x3c288b49), unchecked((int) 0x0dff4195), unchecked((int) 0xa8397101), unchecked((int) 0x0c08deb3), + unchecked((int) 0xb4d89ce4), unchecked((int) 0x566490c1), unchecked((int) 0xcb7b6184), unchecked((int) 0x32d570b6), unchecked((int) 0x6c48745c), + unchecked((int) 0xb8d04257)}; + + private static readonly int[] Tinv3 = + { + unchecked((int) 0x5150a7f4), unchecked((int) 0x7e536541), unchecked((int) 0x1ac3a417), unchecked((int) 0x3a965e27), unchecked((int) 0x3bcb6bab), + unchecked((int) 0x1ff1459d), unchecked((int) 0xacab58fa), unchecked((int) 0x4b9303e3), unchecked((int) 0x2055fa30), unchecked((int) 0xadf66d76), + unchecked((int) 0x889176cc), unchecked((int) 0xf5254c02), unchecked((int) 0x4ffcd7e5), unchecked((int) 0xc5d7cb2a), unchecked((int) 0x26804435), + unchecked((int) 0xb58fa362), unchecked((int) 0xde495ab1), unchecked((int) 0x25671bba), unchecked((int) 0x45980eea), unchecked((int) 0x5de1c0fe), + unchecked((int) 0xc302752f), unchecked((int) 0x8112f04c), unchecked((int) 0x8da39746), unchecked((int) 0x6bc6f9d3), unchecked((int) 0x03e75f8f), + unchecked((int) 0x15959c92), unchecked((int) 0xbfeb7a6d), unchecked((int) 0x95da5952), unchecked((int) 0xd42d83be), unchecked((int) 0x58d32174), + unchecked((int) 0x492969e0), unchecked((int) 0x8e44c8c9), unchecked((int) 0x756a89c2), unchecked((int) 0xf478798e), unchecked((int) 0x996b3e58), + unchecked((int) 0x27dd71b9), unchecked((int) 0xbeb64fe1), unchecked((int) 0xf017ad88), unchecked((int) 0xc966ac20), unchecked((int) 0x7db43ace), + unchecked((int) 0x63184adf), unchecked((int) 0xe582311a), unchecked((int) 0x97603351), unchecked((int) 0x62457f53), unchecked((int) 0xb1e07764), + unchecked((int) 0xbb84ae6b), unchecked((int) 0xfe1ca081), unchecked((int) 0xf9942b08), unchecked((int) 0x70586848), unchecked((int) 0x8f19fd45), + unchecked((int) 0x94876cde), unchecked((int) 0x52b7f87b), unchecked((int) 0xab23d373), unchecked((int) 0x72e2024b), unchecked((int) 0xe3578f1f), + unchecked((int) 0x662aab55), unchecked((int) 0xb20728eb), unchecked((int) 0x2f03c2b5), unchecked((int) 0x869a7bc5), unchecked((int) 0xd3a50837), + unchecked((int) 0x30f28728), unchecked((int) 0x23b2a5bf), unchecked((int) 0x02ba6a03), unchecked((int) 0xed5c8216), unchecked((int) 0x8a2b1ccf), + unchecked((int) 0xa792b479), unchecked((int) 0xf3f0f207), unchecked((int) 0x4ea1e269), unchecked((int) 0x65cdf4da), unchecked((int) 0x06d5be05), + unchecked((int) 0xd11f6234), unchecked((int) 0xc48afea6), unchecked((int) 0x349d532e), unchecked((int) 0xa2a055f3), unchecked((int) 0x0532e18a), + unchecked((int) 0xa475ebf6), unchecked((int) 0x0b39ec83), unchecked((int) 0x40aaef60), unchecked((int) 0x5e069f71), unchecked((int) 0xbd51106e), + unchecked((int) 0x3ef98a21), unchecked((int) 0x963d06dd), unchecked((int) 0xddae053e), unchecked((int) 0x4d46bde6), unchecked((int) 0x91b58d54), + unchecked((int) 0x71055dc4), unchecked((int) 0x046fd406), unchecked((int) 0x60ff1550), unchecked((int) 0x1924fb98), unchecked((int) 0xd697e9bd), + unchecked((int) 0x89cc4340), unchecked((int) 0x67779ed9), unchecked((int) 0xb0bd42e8), unchecked((int) 0x07888b89), unchecked((int) 0xe7385b19), + unchecked((int) 0x79dbeec8), unchecked((int) 0xa1470a7c), unchecked((int) 0x7ce90f42), unchecked((int) 0xf8c91e84), unchecked((int) 0x00000000), + unchecked((int) 0x09838680), unchecked((int) 0x3248ed2b), unchecked((int) 0x1eac7011), unchecked((int) 0x6c4e725a), unchecked((int) 0xfdfbff0e), + unchecked((int) 0x0f563885), unchecked((int) 0x3d1ed5ae), unchecked((int) 0x3627392d), unchecked((int) 0x0a64d90f), unchecked((int) 0x6821a65c), + unchecked((int) 0x9bd1545b), unchecked((int) 0x243a2e36), unchecked((int) 0x0cb1670a), unchecked((int) 0x930fe757), unchecked((int) 0xb4d296ee), + unchecked((int) 0x1b9e919b), unchecked((int) 0x804fc5c0), unchecked((int) 0x61a220dc), unchecked((int) 0x5a694b77), unchecked((int) 0x1c161a12), + unchecked((int) 0xe20aba93), unchecked((int) 0xc0e52aa0), unchecked((int) 0x3c43e022), unchecked((int) 0x121d171b), unchecked((int) 0x0e0b0d09), + unchecked((int) 0xf2adc78b), unchecked((int) 0x2db9a8b6), unchecked((int) 0x14c8a91e), unchecked((int) 0x578519f1), unchecked((int) 0xaf4c0775), + unchecked((int) 0xeebbdd99), unchecked((int) 0xa3fd607f), unchecked((int) 0xf79f2601), unchecked((int) 0x5cbcf572), unchecked((int) 0x44c53b66), + unchecked((int) 0x5b347efb), unchecked((int) 0x8b762943), unchecked((int) 0xcbdcc623), unchecked((int) 0xb668fced), unchecked((int) 0xb863f1e4), + unchecked((int) 0xd7cadc31), unchecked((int) 0x42108563), unchecked((int) 0x13402297), unchecked((int) 0x842011c6), unchecked((int) 0x857d244a), + unchecked((int) 0xd2f83dbb), unchecked((int) 0xae1132f9), unchecked((int) 0xc76da129), unchecked((int) 0x1d4b2f9e), unchecked((int) 0xdcf330b2), + unchecked((int) 0x0dec5286), unchecked((int) 0x77d0e3c1), unchecked((int) 0x2b6c16b3), unchecked((int) 0xa999b970), unchecked((int) 0x11fa4894), + unchecked((int) 0x472264e9), unchecked((int) 0xa8c48cfc), unchecked((int) 0xa01a3ff0), unchecked((int) 0x56d82c7d), unchecked((int) 0x22ef9033), + unchecked((int) 0x87c74e49), unchecked((int) 0xd9c1d138), unchecked((int) 0x8cfea2ca), unchecked((int) 0x98360bd4), unchecked((int) 0xa6cf81f5), + unchecked((int) 0xa528de7a), unchecked((int) 0xda268eb7), unchecked((int) 0x3fa4bfad), unchecked((int) 0x2ce49d3a), unchecked((int) 0x500d9278), + unchecked((int) 0x6a9bcc5f), unchecked((int) 0x5462467e), unchecked((int) 0xf6c2138d), unchecked((int) 0x90e8b8d8), unchecked((int) 0x2e5ef739), + unchecked((int) 0x82f5afc3), unchecked((int) 0x9fbe805d), unchecked((int) 0x697c93d0), unchecked((int) 0x6fa92dd5), unchecked((int) 0xcfb31225), + unchecked((int) 0xc83b99ac), unchecked((int) 0x10a77d18), unchecked((int) 0xe86e639c), unchecked((int) 0xdb7bbb3b), unchecked((int) 0xcd097826), + unchecked((int) 0x6ef41859), unchecked((int) 0xec01b79a), unchecked((int) 0x83a89a4f), unchecked((int) 0xe6656e95), unchecked((int) 0xaa7ee6ff), + unchecked((int) 0x2108cfbc), unchecked((int) 0xefe6e815), unchecked((int) 0xbad99be7), unchecked((int) 0x4ace366f), unchecked((int) 0xead4099f), + unchecked((int) 0x29d67cb0), unchecked((int) 0x31afb2a4), unchecked((int) 0x2a31233f), unchecked((int) 0xc63094a5), unchecked((int) 0x35c066a2), + unchecked((int) 0x7437bc4e), unchecked((int) 0xfca6ca82), unchecked((int) 0xe0b0d090), unchecked((int) 0x3315d8a7), unchecked((int) 0xf14a9804), + unchecked((int) 0x41f7daec), unchecked((int) 0x7f0e50cd), unchecked((int) 0x172ff691), unchecked((int) 0x768dd64d), unchecked((int) 0x434db0ef), + unchecked((int) 0xcc544daa), unchecked((int) 0xe4df0496), unchecked((int) 0x9ee3b5d1), unchecked((int) 0x4c1b886a), unchecked((int) 0xc1b81f2c), + unchecked((int) 0x467f5165), unchecked((int) 0x9d04ea5e), unchecked((int) 0x015d358c), unchecked((int) 0xfa737487), unchecked((int) 0xfb2e410b), + unchecked((int) 0xb35a1d67), unchecked((int) 0x9252d2db), unchecked((int) 0xe9335610), unchecked((int) 0x6d1347d6), unchecked((int) 0x9a8c61d7), + unchecked((int) 0x377a0ca1), unchecked((int) 0x598e14f8), unchecked((int) 0xeb893c13), unchecked((int) 0xceee27a9), unchecked((int) 0xb735c961), + unchecked((int) 0xe1ede51c), unchecked((int) 0x7a3cb147), unchecked((int) 0x9c59dfd2), unchecked((int) 0x553f73f2), unchecked((int) 0x1879ce14), + unchecked((int) 0x73bf37c7), unchecked((int) 0x53eacdf7), unchecked((int) 0x5f5baafd), unchecked((int) 0xdf146f3d), unchecked((int) 0x7886db44), + unchecked((int) 0xca81f3af), unchecked((int) 0xb93ec468), unchecked((int) 0x382c3424), unchecked((int) 0xc25f40a3), unchecked((int) 0x1672c31d), + unchecked((int) 0xbc0c25e2), unchecked((int) 0x288b493c), unchecked((int) 0xff41950d), unchecked((int) 0x397101a8), unchecked((int) 0x08deb30c), + unchecked((int) 0xd89ce4b4), unchecked((int) 0x6490c156), unchecked((int) 0x7b6184cb), unchecked((int) 0xd570b632), unchecked((int) 0x48745c6c), + unchecked((int) 0xd04257b8)}; + + private int Shift( + int r, + int shift) + { + return ((int) ( ( (uint) r >> shift) | + (uint) (r << (32 - shift)) + )); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const int m1 = unchecked((int) 0x80808080); + private const int m2 = unchecked((int) 0x7f7f7f7f); + private const int m3 = unchecked((int) 0x0000001b); + private int FFmulX(int x) { + return ( (int) ( ((x & m2) << 1) ^ + (( (uint)(x & m1) >> 7) * m3) + ) ); + } + + /* + The following defines provide alternative definitions of FFmulX that might + give improved performance if a fast 32-bit multiply is not available. + + private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } + private static final int m4 = 0x1b1b1b1b; + private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } + + */ + + private int Inv_Mcol(int x) { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + + private 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); + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on key size and block size + * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits + * This code is written assuming those are the only possible values + */ + private int[,] GenerateWorkingKey( + byte[] key, + bool forEncryption) + { + int KC = key.Length / 4; // key length in words + int t; + + if ((KC != 4) && (KC != 6) && (KC != 8)) { + throw new ArgumentException("Key length not 128/192/256 bits."); + } + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + int[,] W = new int[ROUNDS+1,4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + for (int i = 0; i < key.Length; t++) + { + W[t >> 2,t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + int temp = W[(i-1)>>2,(i-1)&3]; + if ((i % KC) == 0) { + temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC)-1]; + } else if ((KC > 6) && ((i % KC) == 4)) { + temp = SubWord(temp); + } + + W[i>>2,i&3] = W[(i - KC)>>2,(i-KC)&3] ^ temp; + } + + if (!forEncryption) { + for (int j = 1; j < ROUNDS; j++) { + for (int i = 0; i < 4; i++){ + W[j,i] = Inv_Mcol(W[j,i]); + } + } + } + + return W; + } + + private int ROUNDS; + private int[,] WorkingKey; + private int C0, C1, C2, C3; + private bool forEncryption; + + private const int BLOCK_SIZE = 16; + + /** + * default constructor - 128 bit block size. + */ + public AesFastEngine() + { + } + + /** + * initialise an AES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().ToString()); + + WorkingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey(), forEncryption); + this.forEncryption = forEncryption; + } + + public string AlgorithmName + { + get { return "AES"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (WorkingKey == null) + { + throw new InvalidOperationException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (forEncryption) + { + UnPackBlock(input, inOff); + EncryptBlock(WorkingKey); + PackBlock(output, outOff); + } + else + { + UnPackBlock(input, inOff); + DecryptBlock(WorkingKey); + PackBlock(output, outOff); + } + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + 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; + } + + 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); + } + + 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]; + + for (r = 1; 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]; + } + + 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]; + + // 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]; + + } + + 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]; + + for (r = ROUNDS-1; 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]; + } + + 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]; + + // 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]; + } + } +} diff --git a/src/core/srcbc/crypto/engines/AesLightEngine.cs b/src/core/srcbc/crypto/engines/AesLightEngine.cs new file mode 100644 index 0000000..57ec6f7 --- /dev/null +++ b/src/core/srcbc/crypto/engines/AesLightEngine.cs @@ -0,0 +1,438 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of the AES (Rijndael), from FIPS-197. + *

+ * For further details see: http://csrc.nist.gov/encryption/aes/. + * + * This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at + * http://fp.gladman.plus.com/cryptography_technology/rijndael/ + * + * There are three levels of tradeoff of speed vs memory + * Because java has no preprocessor, they are written as three separate classes from which to choose + * + * The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption + * and 4 for decryption. + * + * The middle performance version uses only one 256 word table for each, for a total of 2Kbytes, + * adding 12 rotate operations per round to compute the values contained in the other tables from + * the contents of the first + * + * The slowest version uses no static tables at all and computes the values + * in each round. + *

+ *

+ * This file contains the slowest performance version with no static tables + * for round precomputation, but it has the smallest foot print. + *

+ */ + public class AesLightEngine + : IBlockCipher + { + // The S box + private static readonly byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, + (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, + (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, + (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, + (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, + (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, + (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, + (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, + (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, + (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, + (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, + (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, + (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, + (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, + (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, + (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, + (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + // The inverse S-box + private static readonly byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, + (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, + (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, + (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, + (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, + (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, + (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, + (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, + (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, + (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, + (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, + (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, + (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, + (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, + (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, + (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, + (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly int[] rcon = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + private int Shift( + int r, + int shift) + { + return ((int) ( ( (uint) r >> shift) | + (uint) (r << (32 - shift)) ) + ); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const int m1 = unchecked((int) 0x80808080); + private const int m2 = unchecked((int) 0x7f7f7f7f); + private const int m3 = unchecked((int) 0x0000001b); + + private int FFmulX(int x) + { + return ( (int) ( ((x & m2) << 1) ^ + (( (uint)(x & m1) >> 7) * m3) + ) + ); + } + + /* + The following defines provide alternative definitions of FFmulX that might + give improved performance if a fast 32-bit multiply is not available. + + private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); } + private static final int m4 = 0x1b1b1b1b; + private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); } + + */ + + private int Mcol(int x) + { + int f2 = FFmulX(x); + return f2 ^ Shift(x ^ f2, 8) ^ Shift(x, 16) ^ Shift(x, 24); + } + + private int Inv_Mcol(int x) + { + int f2 = FFmulX(x); + int f4 = FFmulX(f2); + int f8 = FFmulX(f4); + int f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + + private 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); + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on key size and block size + * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits + * This code is written assuming those are the only possible values + */ + private int[,] GenerateWorkingKey( + byte[] key, + bool forEncryption) + { + int KC = key.Length / 4; // key length in words + int t; + + if ((KC != 4) && (KC != 6) && (KC != 8)) { + throw new ArgumentException("Key length not 128/192/256 bits."); + } + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + int[,] W = new int[ROUNDS+1,4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + for (int i = 0; i < key.Length; t++) + { + W[t >> 2,t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + int temp = W[(i-1)>>2,(i-1)&3]; + if ((i % KC) == 0) { + temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC)-1]; + } else if ((KC > 6) && ((i % KC) == 4)) { + temp = SubWord(temp); + } + + W[i>>2,i&3] = W[(i - KC)>>2,(i-KC)&3] ^ temp; + } + + if (!forEncryption) { + for (int j = 1; j < ROUNDS; j++) { + for (int i = 0; i < 4; i++){ + W[j,i] = Inv_Mcol(W[j,i]); + } + } + } + + return W; + } + + private int ROUNDS; + private int[,] WorkingKey; + private int C0, C1, C2, C3; + private bool forEncryption; + + private const int BLOCK_SIZE = 16; + + /** + * default constructor - 128 bit block size. + */ + public AesLightEngine() + { + } + + /** + * initialise an AES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().ToString()); + + WorkingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey(), forEncryption); + this.forEncryption = forEncryption; + } + + public string AlgorithmName + { + get { return "AES"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (WorkingKey == null) + { + throw new InvalidOperationException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (forEncryption) + { + UnPackBlock(input, inOff); + EncryptBlock(WorkingKey); + PackBlock(output, outOff); + } + else + { + UnPackBlock(input, inOff); + DecryptBlock(WorkingKey); + PackBlock(output, outOff); + } + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + 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; + } + + 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); + } + + 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]; + + for (r = 1; 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[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]; + + // 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]; + + } + + 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]; + + for (r = ROUNDS-1; 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[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]; + + // 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]; + } + } + +} diff --git a/src/core/srcbc/crypto/engines/AesWrapEngine.cs b/src/core/srcbc/crypto/engines/AesWrapEngine.cs new file mode 100644 index 0000000..bfba7cd --- /dev/null +++ b/src/core/srcbc/crypto/engines/AesWrapEngine.cs @@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Engines +{ + /// + /// An implementation of the AES Key Wrapper from the NIST Key Wrap Specification. + ///

+ /// For further details see: http://csrc.nist.gov/encryption/kms/key-wrap.pdf. + /// + public class AesWrapEngine + : Rfc3394WrapEngine + { + public AesWrapEngine() + : base(new AesEngine()) + { + } + } +} diff --git a/src/core/srcbc/crypto/engines/BlowfishEngine.cs b/src/core/srcbc/crypto/engines/BlowfishEngine.cs new file mode 100644 index 0000000..0bfdf70 --- /dev/null +++ b/src/core/srcbc/crypto/engines/BlowfishEngine.cs @@ -0,0 +1,577 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * A class that provides Blowfish key encryption operations, + * such as encoding data and generating keys. + * All the algorithms herein are from Applied Cryptography + * and implement a simplified cryptography interface. + */ + public sealed class BlowfishEngine + : IBlockCipher + { + private readonly static int[] + KP = { + unchecked((int) 0x243F6A88), unchecked((int) 0x85A308D3), unchecked((int) 0x13198A2E), unchecked((int) 0x03707344), + unchecked((int) 0xA4093822), unchecked((int) 0x299F31D0), unchecked((int) 0x082EFA98), unchecked((int) 0xEC4E6C89), + unchecked((int) 0x452821E6), unchecked((int) 0x38D01377), unchecked((int) 0xBE5466CF), unchecked((int) 0x34E90C6C), + unchecked((int) 0xC0AC29B7), unchecked((int) 0xC97C50DD), unchecked((int) 0x3F84D5B5), unchecked((int) 0xB5470917), + unchecked((int) 0x9216D5D9), unchecked((int) 0x8979FB1B) + }, + + KS0 = { + unchecked((int) 0xD1310BA6), unchecked((int) 0x98DFB5AC), unchecked((int) 0x2FFD72DB), unchecked((int) 0xD01ADFB7), + unchecked((int) 0xB8E1AFED), unchecked((int) 0x6A267E96), unchecked((int) 0xBA7C9045), unchecked((int) 0xF12C7F99), + unchecked((int) 0x24A19947), unchecked((int) 0xB3916CF7), unchecked((int) 0x0801F2E2), unchecked((int) 0x858EFC16), + unchecked((int) 0x636920D8), unchecked((int) 0x71574E69), unchecked((int) 0xA458FEA3), unchecked((int) 0xF4933D7E), + unchecked((int) 0x0D95748F), unchecked((int) 0x728EB658), unchecked((int) 0x718BCD58), unchecked((int) 0x82154AEE), + unchecked((int) 0x7B54A41D), unchecked((int) 0xC25A59B5), unchecked((int) 0x9C30D539), unchecked((int) 0x2AF26013), + unchecked((int) 0xC5D1B023), unchecked((int) 0x286085F0), unchecked((int) 0xCA417918), unchecked((int) 0xB8DB38EF), + unchecked((int) 0x8E79DCB0), unchecked((int) 0x603A180E), unchecked((int) 0x6C9E0E8B), unchecked((int) 0xB01E8A3E), + unchecked((int) 0xD71577C1), unchecked((int) 0xBD314B27), unchecked((int) 0x78AF2FDA), unchecked((int) 0x55605C60), + unchecked((int) 0xE65525F3), unchecked((int) 0xAA55AB94), unchecked((int) 0x57489862), unchecked((int) 0x63E81440), + unchecked((int) 0x55CA396A), unchecked((int) 0x2AAB10B6), unchecked((int) 0xB4CC5C34), unchecked((int) 0x1141E8CE), + unchecked((int) 0xA15486AF), unchecked((int) 0x7C72E993), unchecked((int) 0xB3EE1411), unchecked((int) 0x636FBC2A), + unchecked((int) 0x2BA9C55D), unchecked((int) 0x741831F6), unchecked((int) 0xCE5C3E16), unchecked((int) 0x9B87931E), + unchecked((int) 0xAFD6BA33), unchecked((int) 0x6C24CF5C), unchecked((int) 0x7A325381), unchecked((int) 0x28958677), + unchecked((int) 0x3B8F4898), unchecked((int) 0x6B4BB9AF), unchecked((int) 0xC4BFE81B), unchecked((int) 0x66282193), + unchecked((int) 0x61D809CC), unchecked((int) 0xFB21A991), unchecked((int) 0x487CAC60), unchecked((int) 0x5DEC8032), + unchecked((int) 0xEF845D5D), unchecked((int) 0xE98575B1), unchecked((int) 0xDC262302), unchecked((int) 0xEB651B88), + unchecked((int) 0x23893E81), unchecked((int) 0xD396ACC5), unchecked((int) 0x0F6D6FF3), unchecked((int) 0x83F44239), + unchecked((int) 0x2E0B4482), unchecked((int) 0xA4842004), unchecked((int) 0x69C8F04A), unchecked((int) 0x9E1F9B5E), + unchecked((int) 0x21C66842), unchecked((int) 0xF6E96C9A), unchecked((int) 0x670C9C61), unchecked((int) 0xABD388F0), + unchecked((int) 0x6A51A0D2), unchecked((int) 0xD8542F68), unchecked((int) 0x960FA728), unchecked((int) 0xAB5133A3), + unchecked((int) 0x6EEF0B6C), unchecked((int) 0x137A3BE4), unchecked((int) 0xBA3BF050), unchecked((int) 0x7EFB2A98), + unchecked((int) 0xA1F1651D), unchecked((int) 0x39AF0176), unchecked((int) 0x66CA593E), unchecked((int) 0x82430E88), + unchecked((int) 0x8CEE8619), unchecked((int) 0x456F9FB4), unchecked((int) 0x7D84A5C3), unchecked((int) 0x3B8B5EBE), + unchecked((int) 0xE06F75D8), unchecked((int) 0x85C12073), unchecked((int) 0x401A449F), unchecked((int) 0x56C16AA6), + unchecked((int) 0x4ED3AA62), unchecked((int) 0x363F7706), unchecked((int) 0x1BFEDF72), unchecked((int) 0x429B023D), + unchecked((int) 0x37D0D724), unchecked((int) 0xD00A1248), unchecked((int) 0xDB0FEAD3), unchecked((int) 0x49F1C09B), + unchecked((int) 0x075372C9), unchecked((int) 0x80991B7B), unchecked((int) 0x25D479D8), unchecked((int) 0xF6E8DEF7), + unchecked((int) 0xE3FE501A), unchecked((int) 0xB6794C3B), unchecked((int) 0x976CE0BD), unchecked((int) 0x04C006BA), + unchecked((int) 0xC1A94FB6), unchecked((int) 0x409F60C4), unchecked((int) 0x5E5C9EC2), unchecked((int) 0x196A2463), + unchecked((int) 0x68FB6FAF), unchecked((int) 0x3E6C53B5), unchecked((int) 0x1339B2EB), unchecked((int) 0x3B52EC6F), + unchecked((int) 0x6DFC511F), unchecked((int) 0x9B30952C), unchecked((int) 0xCC814544), unchecked((int) 0xAF5EBD09), + unchecked((int) 0xBEE3D004), unchecked((int) 0xDE334AFD), unchecked((int) 0x660F2807), unchecked((int) 0x192E4BB3), + unchecked((int) 0xC0CBA857), unchecked((int) 0x45C8740F), unchecked((int) 0xD20B5F39), unchecked((int) 0xB9D3FBDB), + unchecked((int) 0x5579C0BD), unchecked((int) 0x1A60320A), unchecked((int) 0xD6A100C6), unchecked((int) 0x402C7279), + unchecked((int) 0x679F25FE), unchecked((int) 0xFB1FA3CC), unchecked((int) 0x8EA5E9F8), unchecked((int) 0xDB3222F8), + unchecked((int) 0x3C7516DF), unchecked((int) 0xFD616B15), unchecked((int) 0x2F501EC8), unchecked((int) 0xAD0552AB), + unchecked((int) 0x323DB5FA), unchecked((int) 0xFD238760), unchecked((int) 0x53317B48), unchecked((int) 0x3E00DF82), + unchecked((int) 0x9E5C57BB), unchecked((int) 0xCA6F8CA0), unchecked((int) 0x1A87562E), unchecked((int) 0xDF1769DB), + unchecked((int) 0xD542A8F6), unchecked((int) 0x287EFFC3), unchecked((int) 0xAC6732C6), unchecked((int) 0x8C4F5573), + unchecked((int) 0x695B27B0), unchecked((int) 0xBBCA58C8), unchecked((int) 0xE1FFA35D), unchecked((int) 0xB8F011A0), + unchecked((int) 0x10FA3D98), unchecked((int) 0xFD2183B8), unchecked((int) 0x4AFCB56C), unchecked((int) 0x2DD1D35B), + unchecked((int) 0x9A53E479), unchecked((int) 0xB6F84565), unchecked((int) 0xD28E49BC), unchecked((int) 0x4BFB9790), + unchecked((int) 0xE1DDF2DA), unchecked((int) 0xA4CB7E33), unchecked((int) 0x62FB1341), unchecked((int) 0xCEE4C6E8), + unchecked((int) 0xEF20CADA), unchecked((int) 0x36774C01), unchecked((int) 0xD07E9EFE), unchecked((int) 0x2BF11FB4), + unchecked((int) 0x95DBDA4D), unchecked((int) 0xAE909198), unchecked((int) 0xEAAD8E71), unchecked((int) 0x6B93D5A0), + unchecked((int) 0xD08ED1D0), unchecked((int) 0xAFC725E0), unchecked((int) 0x8E3C5B2F), unchecked((int) 0x8E7594B7), + unchecked((int) 0x8FF6E2FB), unchecked((int) 0xF2122B64), unchecked((int) 0x8888B812), unchecked((int) 0x900DF01C), + unchecked((int) 0x4FAD5EA0), unchecked((int) 0x688FC31C), unchecked((int) 0xD1CFF191), unchecked((int) 0xB3A8C1AD), + unchecked((int) 0x2F2F2218), unchecked((int) 0xBE0E1777), unchecked((int) 0xEA752DFE), unchecked((int) 0x8B021FA1), + unchecked((int) 0xE5A0CC0F), unchecked((int) 0xB56F74E8), unchecked((int) 0x18ACF3D6), unchecked((int) 0xCE89E299), + unchecked((int) 0xB4A84FE0), unchecked((int) 0xFD13E0B7), unchecked((int) 0x7CC43B81), unchecked((int) 0xD2ADA8D9), + unchecked((int) 0x165FA266), unchecked((int) 0x80957705), unchecked((int) 0x93CC7314), unchecked((int) 0x211A1477), + unchecked((int) 0xE6AD2065), unchecked((int) 0x77B5FA86), unchecked((int) 0xC75442F5), unchecked((int) 0xFB9D35CF), + unchecked((int) 0xEBCDAF0C), unchecked((int) 0x7B3E89A0), unchecked((int) 0xD6411BD3), unchecked((int) 0xAE1E7E49), + unchecked((int) 0x00250E2D), unchecked((int) 0x2071B35E), unchecked((int) 0x226800BB), unchecked((int) 0x57B8E0AF), + unchecked((int) 0x2464369B), unchecked((int) 0xF009B91E), unchecked((int) 0x5563911D), unchecked((int) 0x59DFA6AA), + unchecked((int) 0x78C14389), unchecked((int) 0xD95A537F), unchecked((int) 0x207D5BA2), unchecked((int) 0x02E5B9C5), + unchecked((int) 0x83260376), unchecked((int) 0x6295CFA9), unchecked((int) 0x11C81968), unchecked((int) 0x4E734A41), + unchecked((int) 0xB3472DCA), unchecked((int) 0x7B14A94A), unchecked((int) 0x1B510052), unchecked((int) 0x9A532915), + unchecked((int) 0xD60F573F), unchecked((int) 0xBC9BC6E4), unchecked((int) 0x2B60A476), unchecked((int) 0x81E67400), + unchecked((int) 0x08BA6FB5), unchecked((int) 0x571BE91F), unchecked((int) 0xF296EC6B), unchecked((int) 0x2A0DD915), + unchecked((int) 0xB6636521), unchecked((int) 0xE7B9F9B6), unchecked((int) 0xFF34052E), unchecked((int) 0xC5855664), + unchecked((int) 0x53B02D5D), unchecked((int) 0xA99F8FA1), unchecked((int) 0x08BA4799), unchecked((int) 0x6E85076A) + }, + + KS1 = { + unchecked((int) 0x4B7A70E9), unchecked((int) 0xB5B32944), unchecked((int) 0xDB75092E), unchecked((int) 0xC4192623), + unchecked((int) 0xAD6EA6B0), unchecked((int) 0x49A7DF7D), unchecked((int) 0x9CEE60B8), unchecked((int) 0x8FEDB266), + unchecked((int) 0xECAA8C71), unchecked((int) 0x699A17FF), unchecked((int) 0x5664526C), unchecked((int) 0xC2B19EE1), + unchecked((int) 0x193602A5), unchecked((int) 0x75094C29), unchecked((int) 0xA0591340), unchecked((int) 0xE4183A3E), + unchecked((int) 0x3F54989A), unchecked((int) 0x5B429D65), unchecked((int) 0x6B8FE4D6), unchecked((int) 0x99F73FD6), + unchecked((int) 0xA1D29C07), unchecked((int) 0xEFE830F5), unchecked((int) 0x4D2D38E6), unchecked((int) 0xF0255DC1), + unchecked((int) 0x4CDD2086), unchecked((int) 0x8470EB26), unchecked((int) 0x6382E9C6), unchecked((int) 0x021ECC5E), + unchecked((int) 0x09686B3F), unchecked((int) 0x3EBAEFC9), unchecked((int) 0x3C971814), unchecked((int) 0x6B6A70A1), + unchecked((int) 0x687F3584), unchecked((int) 0x52A0E286), unchecked((int) 0xB79C5305), unchecked((int) 0xAA500737), + unchecked((int) 0x3E07841C), unchecked((int) 0x7FDEAE5C), unchecked((int) 0x8E7D44EC), unchecked((int) 0x5716F2B8), + unchecked((int) 0xB03ADA37), unchecked((int) 0xF0500C0D), unchecked((int) 0xF01C1F04), unchecked((int) 0x0200B3FF), + unchecked((int) 0xAE0CF51A), unchecked((int) 0x3CB574B2), unchecked((int) 0x25837A58), unchecked((int) 0xDC0921BD), + unchecked((int) 0xD19113F9), unchecked((int) 0x7CA92FF6), unchecked((int) 0x94324773), unchecked((int) 0x22F54701), + unchecked((int) 0x3AE5E581), unchecked((int) 0x37C2DADC), unchecked((int) 0xC8B57634), unchecked((int) 0x9AF3DDA7), + unchecked((int) 0xA9446146), unchecked((int) 0x0FD0030E), unchecked((int) 0xECC8C73E), unchecked((int) 0xA4751E41), + unchecked((int) 0xE238CD99), unchecked((int) 0x3BEA0E2F), unchecked((int) 0x3280BBA1), unchecked((int) 0x183EB331), + unchecked((int) 0x4E548B38), unchecked((int) 0x4F6DB908), unchecked((int) 0x6F420D03), unchecked((int) 0xF60A04BF), + unchecked((int) 0x2CB81290), unchecked((int) 0x24977C79), unchecked((int) 0x5679B072), unchecked((int) 0xBCAF89AF), + unchecked((int) 0xDE9A771F), unchecked((int) 0xD9930810), unchecked((int) 0xB38BAE12), unchecked((int) 0xDCCF3F2E), + unchecked((int) 0x5512721F), unchecked((int) 0x2E6B7124), unchecked((int) 0x501ADDE6), unchecked((int) 0x9F84CD87), + unchecked((int) 0x7A584718), unchecked((int) 0x7408DA17), unchecked((int) 0xBC9F9ABC), unchecked((int) 0xE94B7D8C), + unchecked((int) 0xEC7AEC3A), unchecked((int) 0xDB851DFA), unchecked((int) 0x63094366), unchecked((int) 0xC464C3D2), + unchecked((int) 0xEF1C1847), unchecked((int) 0x3215D908), unchecked((int) 0xDD433B37), unchecked((int) 0x24C2BA16), + unchecked((int) 0x12A14D43), unchecked((int) 0x2A65C451), unchecked((int) 0x50940002), unchecked((int) 0x133AE4DD), + unchecked((int) 0x71DFF89E), unchecked((int) 0x10314E55), unchecked((int) 0x81AC77D6), unchecked((int) 0x5F11199B), + unchecked((int) 0x043556F1), unchecked((int) 0xD7A3C76B), unchecked((int) 0x3C11183B), unchecked((int) 0x5924A509), + unchecked((int) 0xF28FE6ED), unchecked((int) 0x97F1FBFA), unchecked((int) 0x9EBABF2C), unchecked((int) 0x1E153C6E), + unchecked((int) 0x86E34570), unchecked((int) 0xEAE96FB1), unchecked((int) 0x860E5E0A), unchecked((int) 0x5A3E2AB3), + unchecked((int) 0x771FE71C), unchecked((int) 0x4E3D06FA), unchecked((int) 0x2965DCB9), unchecked((int) 0x99E71D0F), + unchecked((int) 0x803E89D6), unchecked((int) 0x5266C825), unchecked((int) 0x2E4CC978), unchecked((int) 0x9C10B36A), + unchecked((int) 0xC6150EBA), unchecked((int) 0x94E2EA78), unchecked((int) 0xA5FC3C53), unchecked((int) 0x1E0A2DF4), + unchecked((int) 0xF2F74EA7), unchecked((int) 0x361D2B3D), unchecked((int) 0x1939260F), unchecked((int) 0x19C27960), + unchecked((int) 0x5223A708), unchecked((int) 0xF71312B6), unchecked((int) 0xEBADFE6E), unchecked((int) 0xEAC31F66), + unchecked((int) 0xE3BC4595), unchecked((int) 0xA67BC883), unchecked((int) 0xB17F37D1), unchecked((int) 0x018CFF28), + unchecked((int) 0xC332DDEF), unchecked((int) 0xBE6C5AA5), unchecked((int) 0x65582185), unchecked((int) 0x68AB9802), + unchecked((int) 0xEECEA50F), unchecked((int) 0xDB2F953B), unchecked((int) 0x2AEF7DAD), unchecked((int) 0x5B6E2F84), + unchecked((int) 0x1521B628), unchecked((int) 0x29076170), unchecked((int) 0xECDD4775), unchecked((int) 0x619F1510), + unchecked((int) 0x13CCA830), unchecked((int) 0xEB61BD96), unchecked((int) 0x0334FE1E), unchecked((int) 0xAA0363CF), + unchecked((int) 0xB5735C90), unchecked((int) 0x4C70A239), unchecked((int) 0xD59E9E0B), unchecked((int) 0xCBAADE14), + unchecked((int) 0xEECC86BC), unchecked((int) 0x60622CA7), unchecked((int) 0x9CAB5CAB), unchecked((int) 0xB2F3846E), + unchecked((int) 0x648B1EAF), unchecked((int) 0x19BDF0CA), unchecked((int) 0xA02369B9), unchecked((int) 0x655ABB50), + unchecked((int) 0x40685A32), unchecked((int) 0x3C2AB4B3), unchecked((int) 0x319EE9D5), unchecked((int) 0xC021B8F7), + unchecked((int) 0x9B540B19), unchecked((int) 0x875FA099), unchecked((int) 0x95F7997E), unchecked((int) 0x623D7DA8), + unchecked((int) 0xF837889A), unchecked((int) 0x97E32D77), unchecked((int) 0x11ED935F), unchecked((int) 0x16681281), + unchecked((int) 0x0E358829), unchecked((int) 0xC7E61FD6), unchecked((int) 0x96DEDFA1), unchecked((int) 0x7858BA99), + unchecked((int) 0x57F584A5), unchecked((int) 0x1B227263), unchecked((int) 0x9B83C3FF), unchecked((int) 0x1AC24696), + unchecked((int) 0xCDB30AEB), unchecked((int) 0x532E3054), unchecked((int) 0x8FD948E4), unchecked((int) 0x6DBC3128), + unchecked((int) 0x58EBF2EF), unchecked((int) 0x34C6FFEA), unchecked((int) 0xFE28ED61), unchecked((int) 0xEE7C3C73), + unchecked((int) 0x5D4A14D9), unchecked((int) 0xE864B7E3), unchecked((int) 0x42105D14), unchecked((int) 0x203E13E0), + unchecked((int) 0x45EEE2B6), unchecked((int) 0xA3AAABEA), unchecked((int) 0xDB6C4F15), unchecked((int) 0xFACB4FD0), + unchecked((int) 0xC742F442), unchecked((int) 0xEF6ABBB5), unchecked((int) 0x654F3B1D), unchecked((int) 0x41CD2105), + unchecked((int) 0xD81E799E), unchecked((int) 0x86854DC7), unchecked((int) 0xE44B476A), unchecked((int) 0x3D816250), + unchecked((int) 0xCF62A1F2), unchecked((int) 0x5B8D2646), unchecked((int) 0xFC8883A0), unchecked((int) 0xC1C7B6A3), + unchecked((int) 0x7F1524C3), unchecked((int) 0x69CB7492), unchecked((int) 0x47848A0B), unchecked((int) 0x5692B285), + unchecked((int) 0x095BBF00), unchecked((int) 0xAD19489D), unchecked((int) 0x1462B174), unchecked((int) 0x23820E00), + unchecked((int) 0x58428D2A), unchecked((int) 0x0C55F5EA), unchecked((int) 0x1DADF43E), unchecked((int) 0x233F7061), + unchecked((int) 0x3372F092), unchecked((int) 0x8D937E41), unchecked((int) 0xD65FECF1), unchecked((int) 0x6C223BDB), + unchecked((int) 0x7CDE3759), unchecked((int) 0xCBEE7460), unchecked((int) 0x4085F2A7), unchecked((int) 0xCE77326E), + unchecked((int) 0xA6078084), unchecked((int) 0x19F8509E), unchecked((int) 0xE8EFD855), unchecked((int) 0x61D99735), + unchecked((int) 0xA969A7AA), unchecked((int) 0xC50C06C2), unchecked((int) 0x5A04ABFC), unchecked((int) 0x800BCADC), + unchecked((int) 0x9E447A2E), unchecked((int) 0xC3453484), unchecked((int) 0xFDD56705), unchecked((int) 0x0E1E9EC9), + unchecked((int) 0xDB73DBD3), unchecked((int) 0x105588CD), unchecked((int) 0x675FDA79), unchecked((int) 0xE3674340), + unchecked((int) 0xC5C43465), unchecked((int) 0x713E38D8), unchecked((int) 0x3D28F89E), unchecked((int) 0xF16DFF20), + unchecked((int) 0x153E21E7), unchecked((int) 0x8FB03D4A), unchecked((int) 0xE6E39F2B), unchecked((int) 0xDB83ADF7) + }, + + KS2 = { + unchecked((int) 0xE93D5A68), unchecked((int) 0x948140F7), unchecked((int) 0xF64C261C), unchecked((int) 0x94692934), + unchecked((int) 0x411520F7), unchecked((int) 0x7602D4F7), unchecked((int) 0xBCF46B2E), unchecked((int) 0xD4A20068), + unchecked((int) 0xD4082471), unchecked((int) 0x3320F46A), unchecked((int) 0x43B7D4B7), unchecked((int) 0x500061AF), + unchecked((int) 0x1E39F62E), unchecked((int) 0x97244546), unchecked((int) 0x14214F74), unchecked((int) 0xBF8B8840), + unchecked((int) 0x4D95FC1D), unchecked((int) 0x96B591AF), unchecked((int) 0x70F4DDD3), unchecked((int) 0x66A02F45), + unchecked((int) 0xBFBC09EC), unchecked((int) 0x03BD9785), unchecked((int) 0x7FAC6DD0), unchecked((int) 0x31CB8504), + unchecked((int) 0x96EB27B3), unchecked((int) 0x55FD3941), unchecked((int) 0xDA2547E6), unchecked((int) 0xABCA0A9A), + unchecked((int) 0x28507825), unchecked((int) 0x530429F4), unchecked((int) 0x0A2C86DA), unchecked((int) 0xE9B66DFB), + unchecked((int) 0x68DC1462), unchecked((int) 0xD7486900), unchecked((int) 0x680EC0A4), unchecked((int) 0x27A18DEE), + unchecked((int) 0x4F3FFEA2), unchecked((int) 0xE887AD8C), unchecked((int) 0xB58CE006), unchecked((int) 0x7AF4D6B6), + unchecked((int) 0xAACE1E7C), unchecked((int) 0xD3375FEC), unchecked((int) 0xCE78A399), unchecked((int) 0x406B2A42), + unchecked((int) 0x20FE9E35), unchecked((int) 0xD9F385B9), unchecked((int) 0xEE39D7AB), unchecked((int) 0x3B124E8B), + unchecked((int) 0x1DC9FAF7), unchecked((int) 0x4B6D1856), unchecked((int) 0x26A36631), unchecked((int) 0xEAE397B2), + unchecked((int) 0x3A6EFA74), unchecked((int) 0xDD5B4332), unchecked((int) 0x6841E7F7), unchecked((int) 0xCA7820FB), + unchecked((int) 0xFB0AF54E), unchecked((int) 0xD8FEB397), unchecked((int) 0x454056AC), unchecked((int) 0xBA489527), + unchecked((int) 0x55533A3A), unchecked((int) 0x20838D87), unchecked((int) 0xFE6BA9B7), unchecked((int) 0xD096954B), + unchecked((int) 0x55A867BC), unchecked((int) 0xA1159A58), unchecked((int) 0xCCA92963), unchecked((int) 0x99E1DB33), + unchecked((int) 0xA62A4A56), unchecked((int) 0x3F3125F9), unchecked((int) 0x5EF47E1C), unchecked((int) 0x9029317C), + unchecked((int) 0xFDF8E802), unchecked((int) 0x04272F70), unchecked((int) 0x80BB155C), unchecked((int) 0x05282CE3), + unchecked((int) 0x95C11548), unchecked((int) 0xE4C66D22), unchecked((int) 0x48C1133F), unchecked((int) 0xC70F86DC), + unchecked((int) 0x07F9C9EE), unchecked((int) 0x41041F0F), unchecked((int) 0x404779A4), unchecked((int) 0x5D886E17), + unchecked((int) 0x325F51EB), unchecked((int) 0xD59BC0D1), unchecked((int) 0xF2BCC18F), unchecked((int) 0x41113564), + unchecked((int) 0x257B7834), unchecked((int) 0x602A9C60), unchecked((int) 0xDFF8E8A3), unchecked((int) 0x1F636C1B), + unchecked((int) 0x0E12B4C2), unchecked((int) 0x02E1329E), unchecked((int) 0xAF664FD1), unchecked((int) 0xCAD18115), + unchecked((int) 0x6B2395E0), unchecked((int) 0x333E92E1), unchecked((int) 0x3B240B62), unchecked((int) 0xEEBEB922), + unchecked((int) 0x85B2A20E), unchecked((int) 0xE6BA0D99), unchecked((int) 0xDE720C8C), unchecked((int) 0x2DA2F728), + unchecked((int) 0xD0127845), unchecked((int) 0x95B794FD), unchecked((int) 0x647D0862), unchecked((int) 0xE7CCF5F0), + unchecked((int) 0x5449A36F), unchecked((int) 0x877D48FA), unchecked((int) 0xC39DFD27), unchecked((int) 0xF33E8D1E), + unchecked((int) 0x0A476341), unchecked((int) 0x992EFF74), unchecked((int) 0x3A6F6EAB), unchecked((int) 0xF4F8FD37), + unchecked((int) 0xA812DC60), unchecked((int) 0xA1EBDDF8), unchecked((int) 0x991BE14C), unchecked((int) 0xDB6E6B0D), + unchecked((int) 0xC67B5510), unchecked((int) 0x6D672C37), unchecked((int) 0x2765D43B), unchecked((int) 0xDCD0E804), + unchecked((int) 0xF1290DC7), unchecked((int) 0xCC00FFA3), unchecked((int) 0xB5390F92), unchecked((int) 0x690FED0B), + unchecked((int) 0x667B9FFB), unchecked((int) 0xCEDB7D9C), unchecked((int) 0xA091CF0B), unchecked((int) 0xD9155EA3), + unchecked((int) 0xBB132F88), unchecked((int) 0x515BAD24), unchecked((int) 0x7B9479BF), unchecked((int) 0x763BD6EB), + unchecked((int) 0x37392EB3), unchecked((int) 0xCC115979), unchecked((int) 0x8026E297), unchecked((int) 0xF42E312D), + unchecked((int) 0x6842ADA7), unchecked((int) 0xC66A2B3B), unchecked((int) 0x12754CCC), unchecked((int) 0x782EF11C), + unchecked((int) 0x6A124237), unchecked((int) 0xB79251E7), unchecked((int) 0x06A1BBE6), unchecked((int) 0x4BFB6350), + unchecked((int) 0x1A6B1018), unchecked((int) 0x11CAEDFA), unchecked((int) 0x3D25BDD8), unchecked((int) 0xE2E1C3C9), + unchecked((int) 0x44421659), unchecked((int) 0x0A121386), unchecked((int) 0xD90CEC6E), unchecked((int) 0xD5ABEA2A), + unchecked((int) 0x64AF674E), unchecked((int) 0xDA86A85F), unchecked((int) 0xBEBFE988), unchecked((int) 0x64E4C3FE), + unchecked((int) 0x9DBC8057), unchecked((int) 0xF0F7C086), unchecked((int) 0x60787BF8), unchecked((int) 0x6003604D), + unchecked((int) 0xD1FD8346), unchecked((int) 0xF6381FB0), unchecked((int) 0x7745AE04), unchecked((int) 0xD736FCCC), + unchecked((int) 0x83426B33), unchecked((int) 0xF01EAB71), unchecked((int) 0xB0804187), unchecked((int) 0x3C005E5F), + unchecked((int) 0x77A057BE), unchecked((int) 0xBDE8AE24), unchecked((int) 0x55464299), unchecked((int) 0xBF582E61), + unchecked((int) 0x4E58F48F), unchecked((int) 0xF2DDFDA2), unchecked((int) 0xF474EF38), unchecked((int) 0x8789BDC2), + unchecked((int) 0x5366F9C3), unchecked((int) 0xC8B38E74), unchecked((int) 0xB475F255), unchecked((int) 0x46FCD9B9), + unchecked((int) 0x7AEB2661), unchecked((int) 0x8B1DDF84), unchecked((int) 0x846A0E79), unchecked((int) 0x915F95E2), + unchecked((int) 0x466E598E), unchecked((int) 0x20B45770), unchecked((int) 0x8CD55591), unchecked((int) 0xC902DE4C), + unchecked((int) 0xB90BACE1), unchecked((int) 0xBB8205D0), unchecked((int) 0x11A86248), unchecked((int) 0x7574A99E), + unchecked((int) 0xB77F19B6), unchecked((int) 0xE0A9DC09), unchecked((int) 0x662D09A1), unchecked((int) 0xC4324633), + unchecked((int) 0xE85A1F02), unchecked((int) 0x09F0BE8C), unchecked((int) 0x4A99A025), unchecked((int) 0x1D6EFE10), + unchecked((int) 0x1AB93D1D), unchecked((int) 0x0BA5A4DF), unchecked((int) 0xA186F20F), unchecked((int) 0x2868F169), + unchecked((int) 0xDCB7DA83), unchecked((int) 0x573906FE), unchecked((int) 0xA1E2CE9B), unchecked((int) 0x4FCD7F52), + unchecked((int) 0x50115E01), unchecked((int) 0xA70683FA), unchecked((int) 0xA002B5C4), unchecked((int) 0x0DE6D027), + unchecked((int) 0x9AF88C27), unchecked((int) 0x773F8641), unchecked((int) 0xC3604C06), unchecked((int) 0x61A806B5), + unchecked((int) 0xF0177A28), unchecked((int) 0xC0F586E0), unchecked((int) 0x006058AA), unchecked((int) 0x30DC7D62), + unchecked((int) 0x11E69ED7), unchecked((int) 0x2338EA63), unchecked((int) 0x53C2DD94), unchecked((int) 0xC2C21634), + unchecked((int) 0xBBCBEE56), unchecked((int) 0x90BCB6DE), unchecked((int) 0xEBFC7DA1), unchecked((int) 0xCE591D76), + unchecked((int) 0x6F05E409), unchecked((int) 0x4B7C0188), unchecked((int) 0x39720A3D), unchecked((int) 0x7C927C24), + unchecked((int) 0x86E3725F), unchecked((int) 0x724D9DB9), unchecked((int) 0x1AC15BB4), unchecked((int) 0xD39EB8FC), + unchecked((int) 0xED545578), unchecked((int) 0x08FCA5B5), unchecked((int) 0xD83D7CD3), unchecked((int) 0x4DAD0FC4), + unchecked((int) 0x1E50EF5E), unchecked((int) 0xB161E6F8), unchecked((int) 0xA28514D9), unchecked((int) 0x6C51133C), + unchecked((int) 0x6FD5C7E7), unchecked((int) 0x56E14EC4), unchecked((int) 0x362ABFCE), unchecked((int) 0xDDC6C837), + unchecked((int) 0xD79A3234), unchecked((int) 0x92638212), unchecked((int) 0x670EFA8E), unchecked((int) 0x406000E0) + }, + + KS3 = { + unchecked((int) 0x3A39CE37), unchecked((int) 0xD3FAF5CF), unchecked((int) 0xABC27737), unchecked((int) 0x5AC52D1B), + unchecked((int) 0x5CB0679E), unchecked((int) 0x4FA33742), unchecked((int) 0xD3822740), unchecked((int) 0x99BC9BBE), + unchecked((int) 0xD5118E9D), unchecked((int) 0xBF0F7315), unchecked((int) 0xD62D1C7E), unchecked((int) 0xC700C47B), + unchecked((int) 0xB78C1B6B), unchecked((int) 0x21A19045), unchecked((int) 0xB26EB1BE), unchecked((int) 0x6A366EB4), + unchecked((int) 0x5748AB2F), unchecked((int) 0xBC946E79), unchecked((int) 0xC6A376D2), unchecked((int) 0x6549C2C8), + unchecked((int) 0x530FF8EE), unchecked((int) 0x468DDE7D), unchecked((int) 0xD5730A1D), unchecked((int) 0x4CD04DC6), + unchecked((int) 0x2939BBDB), unchecked((int) 0xA9BA4650), unchecked((int) 0xAC9526E8), unchecked((int) 0xBE5EE304), + unchecked((int) 0xA1FAD5F0), unchecked((int) 0x6A2D519A), unchecked((int) 0x63EF8CE2), unchecked((int) 0x9A86EE22), + unchecked((int) 0xC089C2B8), unchecked((int) 0x43242EF6), unchecked((int) 0xA51E03AA), unchecked((int) 0x9CF2D0A4), + unchecked((int) 0x83C061BA), unchecked((int) 0x9BE96A4D), unchecked((int) 0x8FE51550), unchecked((int) 0xBA645BD6), + unchecked((int) 0x2826A2F9), unchecked((int) 0xA73A3AE1), unchecked((int) 0x4BA99586), unchecked((int) 0xEF5562E9), + unchecked((int) 0xC72FEFD3), unchecked((int) 0xF752F7DA), unchecked((int) 0x3F046F69), unchecked((int) 0x77FA0A59), + unchecked((int) 0x80E4A915), unchecked((int) 0x87B08601), unchecked((int) 0x9B09E6AD), unchecked((int) 0x3B3EE593), + unchecked((int) 0xE990FD5A), unchecked((int) 0x9E34D797), unchecked((int) 0x2CF0B7D9), unchecked((int) 0x022B8B51), + unchecked((int) 0x96D5AC3A), unchecked((int) 0x017DA67D), unchecked((int) 0xD1CF3ED6), unchecked((int) 0x7C7D2D28), + unchecked((int) 0x1F9F25CF), unchecked((int) 0xADF2B89B), unchecked((int) 0x5AD6B472), unchecked((int) 0x5A88F54C), + unchecked((int) 0xE029AC71), unchecked((int) 0xE019A5E6), unchecked((int) 0x47B0ACFD), unchecked((int) 0xED93FA9B), + unchecked((int) 0xE8D3C48D), unchecked((int) 0x283B57CC), unchecked((int) 0xF8D56629), unchecked((int) 0x79132E28), + unchecked((int) 0x785F0191), unchecked((int) 0xED756055), unchecked((int) 0xF7960E44), unchecked((int) 0xE3D35E8C), + unchecked((int) 0x15056DD4), unchecked((int) 0x88F46DBA), unchecked((int) 0x03A16125), unchecked((int) 0x0564F0BD), + unchecked((int) 0xC3EB9E15), unchecked((int) 0x3C9057A2), unchecked((int) 0x97271AEC), unchecked((int) 0xA93A072A), + unchecked((int) 0x1B3F6D9B), unchecked((int) 0x1E6321F5), unchecked((int) 0xF59C66FB), unchecked((int) 0x26DCF319), + unchecked((int) 0x7533D928), unchecked((int) 0xB155FDF5), unchecked((int) 0x03563482), unchecked((int) 0x8ABA3CBB), + unchecked((int) 0x28517711), unchecked((int) 0xC20AD9F8), unchecked((int) 0xABCC5167), unchecked((int) 0xCCAD925F), + unchecked((int) 0x4DE81751), unchecked((int) 0x3830DC8E), unchecked((int) 0x379D5862), unchecked((int) 0x9320F991), + unchecked((int) 0xEA7A90C2), unchecked((int) 0xFB3E7BCE), unchecked((int) 0x5121CE64), unchecked((int) 0x774FBE32), + unchecked((int) 0xA8B6E37E), unchecked((int) 0xC3293D46), unchecked((int) 0x48DE5369), unchecked((int) 0x6413E680), + unchecked((int) 0xA2AE0810), unchecked((int) 0xDD6DB224), unchecked((int) 0x69852DFD), unchecked((int) 0x09072166), + unchecked((int) 0xB39A460A), unchecked((int) 0x6445C0DD), unchecked((int) 0x586CDECF), unchecked((int) 0x1C20C8AE), + unchecked((int) 0x5BBEF7DD), unchecked((int) 0x1B588D40), unchecked((int) 0xCCD2017F), unchecked((int) 0x6BB4E3BB), + unchecked((int) 0xDDA26A7E), unchecked((int) 0x3A59FF45), unchecked((int) 0x3E350A44), unchecked((int) 0xBCB4CDD5), + unchecked((int) 0x72EACEA8), unchecked((int) 0xFA6484BB), unchecked((int) 0x8D6612AE), unchecked((int) 0xBF3C6F47), + unchecked((int) 0xD29BE463), unchecked((int) 0x542F5D9E), unchecked((int) 0xAEC2771B), unchecked((int) 0xF64E6370), + unchecked((int) 0x740E0D8D), unchecked((int) 0xE75B1357), unchecked((int) 0xF8721671), unchecked((int) 0xAF537D5D), + unchecked((int) 0x4040CB08), unchecked((int) 0x4EB4E2CC), unchecked((int) 0x34D2466A), unchecked((int) 0x0115AF84), + unchecked((int) 0xE1B00428), unchecked((int) 0x95983A1D), unchecked((int) 0x06B89FB4), unchecked((int) 0xCE6EA048), + unchecked((int) 0x6F3F3B82), unchecked((int) 0x3520AB82), unchecked((int) 0x011A1D4B), unchecked((int) 0x277227F8), + unchecked((int) 0x611560B1), unchecked((int) 0xE7933FDC), unchecked((int) 0xBB3A792B), unchecked((int) 0x344525BD), + unchecked((int) 0xA08839E1), unchecked((int) 0x51CE794B), unchecked((int) 0x2F32C9B7), unchecked((int) 0xA01FBAC9), + unchecked((int) 0xE01CC87E), unchecked((int) 0xBCC7D1F6), unchecked((int) 0xCF0111C3), unchecked((int) 0xA1E8AAC7), + unchecked((int) 0x1A908749), unchecked((int) 0xD44FBD9A), unchecked((int) 0xD0DADECB), unchecked((int) 0xD50ADA38), + unchecked((int) 0x0339C32A), unchecked((int) 0xC6913667), unchecked((int) 0x8DF9317C), unchecked((int) 0xE0B12B4F), + unchecked((int) 0xF79E59B7), unchecked((int) 0x43F5BB3A), unchecked((int) 0xF2D519FF), unchecked((int) 0x27D9459C), + unchecked((int) 0xBF97222C), unchecked((int) 0x15E6FC2A), unchecked((int) 0x0F91FC71), unchecked((int) 0x9B941525), + unchecked((int) 0xFAE59361), unchecked((int) 0xCEB69CEB), unchecked((int) 0xC2A86459), unchecked((int) 0x12BAA8D1), + unchecked((int) 0xB6C1075E), unchecked((int) 0xE3056A0C), unchecked((int) 0x10D25065), unchecked((int) 0xCB03A442), + unchecked((int) 0xE0EC6E0E), unchecked((int) 0x1698DB3B), unchecked((int) 0x4C98A0BE), unchecked((int) 0x3278E964), + unchecked((int) 0x9F1F9532), unchecked((int) 0xE0D392DF), unchecked((int) 0xD3A0342B), unchecked((int) 0x8971F21E), + unchecked((int) 0x1B0A7441), unchecked((int) 0x4BA3348C), unchecked((int) 0xC5BE7120), unchecked((int) 0xC37632D8), + unchecked((int) 0xDF359F8D), unchecked((int) 0x9B992F2E), unchecked((int) 0xE60B6F47), unchecked((int) 0x0FE3F11D), + unchecked((int) 0xE54CDA54), unchecked((int) 0x1EDAD891), unchecked((int) 0xCE6279CF), unchecked((int) 0xCD3E7E6F), + unchecked((int) 0x1618B166), unchecked((int) 0xFD2C1D05), unchecked((int) 0x848FD2C5), unchecked((int) 0xF6FB2299), + unchecked((int) 0xF523F357), unchecked((int) 0xA6327623), unchecked((int) 0x93A83531), unchecked((int) 0x56CCCD02), + unchecked((int) 0xACF08162), unchecked((int) 0x5A75EBB5), unchecked((int) 0x6E163697), unchecked((int) 0x88D273CC), + unchecked((int) 0xDE966292), unchecked((int) 0x81B949D0), unchecked((int) 0x4C50901B), unchecked((int) 0x71C65614), + unchecked((int) 0xE6C6C7BD), unchecked((int) 0x327A140A), unchecked((int) 0x45E1D006), unchecked((int) 0xC3F27B9A), + unchecked((int) 0xC9AA53FD), unchecked((int) 0x62A80F00), unchecked((int) 0xBB25BFE2), unchecked((int) 0x35BDD2F6), + unchecked((int) 0x71126905), unchecked((int) 0xB2040222), unchecked((int) 0xB6CBCF7C), unchecked((int) 0xCD769C2B), + unchecked((int) 0x53113EC0), unchecked((int) 0x1640E3D3), unchecked((int) 0x38ABBD60), unchecked((int) 0x2547ADF0), + unchecked((int) 0xBA38209C), unchecked((int) 0xF746CE76), unchecked((int) 0x77AFA1C5), unchecked((int) 0x20756060), + unchecked((int) 0x85CBFE4E), unchecked((int) 0x8AE88DD8), unchecked((int) 0x7AAAF9B0), unchecked((int) 0x4CF9AA7E), + unchecked((int) 0x1948C25C), unchecked((int) 0x02FB8A8C), unchecked((int) 0x01C36AE4), unchecked((int) 0xD6EBE1F9), + unchecked((int) 0x90D4F869), unchecked((int) 0xA65CDEA0), unchecked((int) 0x3F09252D), unchecked((int) 0xC208E69F), + unchecked((int) 0xB74E6132), unchecked((int) 0xCE77E25B), unchecked((int) 0x578FDFE3), unchecked((int) 0x3AC372E6) + }; + + //==================================== + // Useful constants + //==================================== + + private static readonly int ROUNDS = 16; + private const int BLOCK_SIZE = 8; // bytes = 64 bits + private static readonly int SBOX_SK = 256; + private static readonly int P_SZ = ROUNDS+2; + + private readonly int[] S0, S1, S2, S3; // the s-boxes + private readonly int[] P; // the p-array + + private bool encrypting; + + private byte[] workingKey; + + public BlowfishEngine() + { + S0 = new int[SBOX_SK]; + S1 = new int[SBOX_SK]; + S2 = new int[SBOX_SK]; + S3 = new int[SBOX_SK]; + P = new int[P_SZ]; + } + + /** + * initialise a Blowfish cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to Blowfish init - " + parameters.GetType().ToString()); + + this.encrypting = forEncryption; + this.workingKey = ((KeyParameter)parameters).GetKey(); + SetKey(this.workingKey); + } + + public string AlgorithmName + { + get { return "Blowfish"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + { + throw new InvalidOperationException("Blowfish not initialised"); + } + + if ((inOff + BLOCK_SIZE) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + BLOCK_SIZE) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, outOff); + } + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + private int F(int x) + { + return (( (S0[((uint) x >> 24)] + S1[((uint) x >> 16) & 0xff]) + ^ S2[((uint) x >> 8) & 0xff]) + S3[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]; + } + } + + private void SetKey(byte[] key) + { + /* + * - comments are from _Applied Crypto_, Schneier, p338 + * please be careful comparing the two, AC numbers the + * arrays from 1, the enclosed code from 0. + * + * (1) + * Initialise the S-boxes and the P-array, with a fixed string + * This string contains the hexadecimal digits of pi (3.141...) + */ + Array.Copy(KS0, 0, S0, 0, SBOX_SK); + Array.Copy(KS1, 0, S1, 0, SBOX_SK); + Array.Copy(KS2, 0, S2, 0, SBOX_SK); + Array.Copy(KS3, 0, S3, 0, SBOX_SK); + + Array.Copy(KP, 0, P, 0, P_SZ); + + /* + * (2) + * Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with the + * second 32-bits of the key, and so on for all bits of the key + * (up to P[17]). Repeatedly cycle through the key bits until the + * entire P-array has been XOR-ed with the key bits + */ + 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 = unchecked((int) 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; + } + + /* + * (3) + * Encrypt the all-zero string with the Blowfish algorithm, using + * the subkeys described in (1) and (2) + * + * (4) + * Replace P1 and P2 with the output of step (3) + * + * (5) + * Encrypt the output of step(3) using the Blowfish algorithm, + * with the modified subkeys. + * + * (6) + * Replace P3 and P4 with the output of step (5) + * + * (7) + * Continue the process, replacing all elements of the P-array + * and then all four S-boxes in order, with the output of the + * continuously changing Blowfish algorithm + */ + + ProcessTable(0, 0, P); + ProcessTable(P[P_SZ - 2], P[P_SZ - 1], S0); + ProcessTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1); + ProcessTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2); + ProcessTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3); + } + + /** + * Encrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * The input will be an exact multiple of our blocksize. + */ + private void EncryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int xl = BytesTo32bits(src, srcIndex); + int xr = BytesTo32bits(src, srcIndex+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]; + + Bits32ToBytes(xr, dst, dstIndex); + Bits32ToBytes(xl, dst, dstIndex + 4); + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * The input will be an exact multiple of our blocksize. + */ + private void DecryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int xl = BytesTo32bits(src, srcIndex); + int xr = BytesTo32bits(src, srcIndex + 4); + + xl ^= P[ROUNDS + 1]; + + for (int i = ROUNDS; i > 0 ; i -= 2) + { + xr ^= F(xl) ^ P[i]; + xl ^= F(xr) ^ P[i - 1]; + } + + xr ^= P[0]; + + Bits32ToBytes(xr, dst, dstIndex); + Bits32ToBytes(xl, dst, dstIndex+4); + } + + 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 inData, byte[] b, int offset) + { + b[offset + 3] = (byte)inData; + b[offset + 2] = (byte)(inData >> 8); + b[offset + 1] = (byte)(inData >> 16); + b[offset] = (byte)(inData >> 24); + } + } +} diff --git a/src/core/srcbc/crypto/engines/CamelliaEngine.cs b/src/core/srcbc/crypto/engines/CamelliaEngine.cs new file mode 100644 index 0000000..6135375 --- /dev/null +++ b/src/core/srcbc/crypto/engines/CamelliaEngine.cs @@ -0,0 +1,579 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Camellia - based on RFC 3713. + */ + public class CamelliaEngine + : IBlockCipher + { + private bool initialised; + private bool _keyIs128; + + private const int BLOCK_SIZE = 16; + + private const long MASK8 = 0xff; + private const long MASK32 = 0xffffffffL; + + private const long SIGMA1 = unchecked((long) 0xA09E667F3BCC908BL); + private const long SIGMA2 = unchecked((long) 0xB67AE8584CAA73B2L); + private const long SIGMA3 = unchecked((long) 0xC6EF372FE94F82BEL); + private const long SIGMA4 = unchecked((long) 0x54FF53A5F1D36F1CL); + private const long SIGMA5 = unchecked((long) 0x10E527FADE682D1DL); + private const long SIGMA6 = unchecked((long) 0xB05688C2B3E6C1FDL); + + private long _kw1, _kw2, _kw3, _kw4; + private long _k1, _k2, _k3, _k4, _k5, _k6, _k7, _k8, _k9, _k10, _k11, _k12, + _k13, _k14, _k15, _k16, _k17, _k18, _k19, _k20, _k21, _k22, _k23, _k24; + private long _ke1, _ke2, _ke3, _ke4, _ke5, _ke6; + + private readonly byte[] SBOX1 = { + (byte)112, (byte)130, (byte)44, (byte)236, (byte)179 , (byte)39, (byte)192, (byte)229, (byte)228, (byte)133 , (byte)87 , (byte)53, (byte)234 , (byte)12, (byte)174 , (byte)65, + (byte)35, (byte)239, (byte)107, (byte)147 , (byte)69 , (byte)25, (byte)165 , (byte)33, (byte)237 , (byte)14 , (byte)79 , (byte)78 , (byte)29, (byte)101, (byte)146, (byte)189, + (byte)134, (byte)184, (byte)175, (byte)143, (byte)124, (byte)235 , (byte)31, (byte)206 , (byte)62 , (byte)48, (byte)220 , (byte)95 , (byte)94, (byte)197 , (byte)11 , (byte)26, + (byte)166, (byte)225, (byte)57, (byte)202, (byte)213 , (byte)71 , (byte)93 , (byte)61, (byte)217 , (byte)1 , (byte)90, (byte)214 , (byte)81 , (byte)86, (byte)108 , (byte)77, + (byte)139, (byte)13, (byte)154, (byte)102, (byte)251, (byte)204, (byte)176 , (byte)45, (byte)116 , (byte)18 , (byte)43 , (byte)32, (byte)240, (byte)177, (byte)132, (byte)153, + (byte)223, (byte)76, (byte)203, (byte)194 , (byte)52, (byte)126, (byte)118 , (byte)5, (byte)109, (byte)183, (byte)169 , (byte)49, (byte)209 , (byte)23 , (byte)4, (byte)215, + (byte)20, (byte)88, (byte)58, (byte)97, (byte)222 , (byte)27 , (byte)17 , (byte)28 , (byte)50 , (byte)15, (byte)156 , (byte)22 , (byte)83 , (byte)24, (byte)242 , (byte)34, + (byte)254, (byte)68, (byte)207, (byte)178, (byte)195, (byte)181, (byte)122, (byte)145 , (byte)36 , (byte)8, (byte)232, (byte)168 , (byte)96, (byte)252, (byte)105 , (byte)80, + (byte)170, (byte)208, (byte)160, (byte)125, (byte)161, (byte)137 , (byte)98, (byte)151 , (byte)84 , (byte)91 , (byte)30, (byte)149, (byte)224, (byte)255, (byte)100, (byte)210, + (byte)16, (byte)196, (byte)0, (byte)72, (byte)163, (byte)247, (byte)117, (byte)219, (byte)138 , (byte)3, (byte)230, (byte)218 , (byte)9 , (byte)63, (byte)221, (byte)148, + (byte)135, (byte)92, (byte)131, (byte)2, (byte)205 , (byte)74, (byte)144 , (byte)51, (byte)115, (byte)103, (byte)246, (byte)243, (byte)157, (byte)127, (byte)191, (byte)226, + (byte)82, (byte)155, (byte)216 , (byte)38, (byte)200 , (byte)55, (byte)198 , (byte)59, (byte)129, (byte)150, (byte)111 , (byte)75 , (byte)19, (byte)190 , (byte)99 , (byte)46, + (byte)233, (byte)121, (byte)167, (byte)140, (byte)159, (byte)110, (byte)188, (byte)142 , (byte)41, (byte)245, (byte)249, (byte)182 , (byte)47, (byte)253, (byte)180 , (byte)89, + (byte)120, (byte)152, (byte)6, (byte)106, (byte)231 , (byte)70, (byte)113, (byte)186, (byte)212 , (byte)37, (byte)171 , (byte)66, (byte)136, (byte)162, (byte)141, (byte)250, + (byte)114, (byte)7, (byte)185 , (byte)85, (byte)248, (byte)238, (byte)172 , (byte)10 , (byte)54 , (byte)73 , (byte)42, (byte)104 , (byte)60 , (byte)56, (byte)241, (byte)164, + (byte)64, (byte)40, (byte)211, (byte)123, (byte)187, (byte)201 , (byte)67, (byte)193 , (byte)21, (byte)227, (byte)173, (byte)244, (byte)119, (byte)199, (byte)128, (byte)158 + }; + + private readonly byte[] SBOX2 = new byte[256]; + private readonly byte[] SBOX3 = new byte[256]; + private readonly byte[] SBOX4 = new byte[256]; + + + public CamelliaEngine() + { + for (int x = 0; x != 256; x++) + { + SBOX2[x] = lRot8(SBOX1[x], 1); + SBOX3[x] = lRot8(SBOX1[x], 7); + SBOX4[x] = SBOX1[lRot8((byte)x, 1) & 0xff]; + } + } + + private void setKey( + bool forEncryption, + byte[] key) + { + long klA, klB; + long krA, krB; + + switch (key.Length) + { + case 16: + _keyIs128 = true; + klA = bytesToWord(key, 0); + klB = bytesToWord(key, 8); + krA = 0; + krB = 0; + break; + case 24: + klA = bytesToWord(key, 0); + klB = bytesToWord(key, 8); + krA = bytesToWord(key, 16); + krB = ~bytesToWord(key, 16); + _keyIs128 = false; + break; + case 32: + klA = bytesToWord(key, 0); + klB = bytesToWord(key, 8); + krA = bytesToWord(key, 16); + krB = bytesToWord(key, 24); + _keyIs128 = false; + break; + default: + throw new ArgumentException("only a key sizes of 128/192/256 are acceptable."); + } + + long d1 = klA ^ krA; + long d2 = klB ^ krB; + + d2 = d2 ^ f(d1, SIGMA1); + d1 = d1 ^ f(d2, SIGMA2); + d1 = d1 ^ klA; + d2 = d2 ^ klB; + d2 = d2 ^ f(d1, SIGMA3); + d1 = d1 ^ f(d2, SIGMA4); + + long kaA = d1; + long kaB = d2; + + if (_keyIs128) + { + if (forEncryption) + { + _kw1 = klA; + _kw2 = klB; + _kw3 = lRot128high(kaA, kaB, 111); + _kw4 = lRot128low(kaA, kaB, 111); + _k1 = kaA; + _k2 = kaB; + _k3 = lRot128high(klA, klB, 15); + _k4 = lRot128low(klA, klB, 15); + _k5 = lRot128high(kaA, kaB, 15); + _k6 = lRot128low(kaA, kaB, 15); + _k7 = lRot128high(klA, klB, 45); + _k8 = lRot128low(klA, klB, 45); + _k9 = lRot128high(kaA, kaB, 45); + _k10 = lRot128low(klA, klB, 60); + _k11 = lRot128high(kaA, kaB, 60); + _k12 = lRot128low(kaA, kaB, 60); + _k13 = lRot128high(klA, klB, 94); + _k14 = lRot128low(klA, klB, 94); + _k15 = lRot128high(kaA, kaB, 94); + _k16 = lRot128low(kaA, kaB, 94); + _k17 = lRot128high(klA, klB, 111); + _k18 = lRot128low(klA, klB, 111); + _ke1 = lRot128high(kaA, kaB, 30); + _ke2 = lRot128low(kaA, kaB, 30); + _ke3 = lRot128high(klA, klB, 77); + _ke4 = lRot128low(klA, klB, 77); + } + else + { + _kw3 = klA; + _kw4 = klB; + _kw1 = lRot128high(kaA, kaB, 111); + _kw2 = lRot128low(kaA, kaB, 111); + _k18 = kaA; + _k17 = kaB; + _k16 = lRot128high(klA, klB, 15); + _k15 = lRot128low(klA, klB, 15); + _k14 = lRot128high(kaA, kaB, 15); + _k13 = lRot128low(kaA, kaB, 15); + _k12 = lRot128high(klA, klB, 45); + _k11 = lRot128low(klA, klB, 45); + _k10 = lRot128high(kaA, kaB, 45); + _k9 = lRot128low(klA, klB, 60); + _k8 = lRot128high(kaA, kaB, 60); + _k7 = lRot128low(kaA, kaB, 60); + _k6 = lRot128high(klA, klB, 94); + _k5 = lRot128low(klA, klB, 94); + _k4 = lRot128high(kaA, kaB, 94); + _k3 = lRot128low(kaA, kaB, 94); + _k2 = lRot128high(klA, klB, 111); + _k1 = lRot128low(klA, klB, 111); + _ke4 = lRot128high(kaA, kaB, 30); + _ke3 = lRot128low(kaA, kaB, 30); + _ke2 = lRot128high(klA, klB, 77); + _ke1 = lRot128low(klA, klB, 77); + } + } + else + { + d1 = kaA ^ krA; + d2 = kaB ^ krB; + d2 = d2 ^ f(d1, SIGMA5); + d1 = d1 ^ f(d2, SIGMA6); + + long kbA = d1; + long kbB = d2; + + if (forEncryption) + { + _kw1 = klA; + _kw2 = klB; + _k1 = kbA; + _k2 = kbB; + _k3 = lRot128high(krA, krB, 15); + _k4 = lRot128low(krA, krB, 15); + _k5 = lRot128high(kaA, kaB, 15); + _k6 = lRot128low(kaA, kaB, 15); + _ke1 = lRot128high(krA, krB, 30); + _ke2 = lRot128low(krA, krB, 30); + _k7 = lRot128high(kbA, kbB, 30); + _k8 = lRot128low(kbA, kbB, 30); + _k9 = lRot128high(klA, klB, 45); + _k10 = lRot128low(klA, klB, 45); + _k11 = lRot128high(kaA, kaB, 45); + _k12 = lRot128low(kaA, kaB, 45); + _ke3 = lRot128high(klA, klB, 60); + _ke4 = lRot128low(klA, klB, 60); + _k13 = lRot128high(krA, krB, 60); + _k14 = lRot128low(krA, krB, 60); + _k15 = lRot128high(kbA, kbB, 60); + _k16 = lRot128low(kbA, kbB, 60); + _k17 = lRot128high(klA, klB, 77); + _k18 = lRot128low(klA, klB, 77); + _ke5 = lRot128high(kaA, kaB, 77); + _ke6 = lRot128low(kaA, kaB, 77); + _k19 = lRot128high(krA, krB, 94); + _k20 = lRot128low(krA, krB, 94); + _k21 = lRot128high(kaA, kaB, 94); + _k22 = lRot128low(kaA, kaB, 94); + _k23 = lRot128high(klA, klB, 111); + _k24 = lRot128low(klA, klB, 111); + _kw3 = lRot128high(kbA, kbB, 111); + _kw4 = lRot128low(kbA, kbB, 111); + } + else + { + _kw3 = klA; + _kw4 = klB; + _kw1 = lRot128high(kbA, kbB, 111); + _kw2 = lRot128low(kbA, kbB, 111); + _k24 = kbA; + _k23 = kbB; + _k22 = lRot128high(krA, krB, 15); + _k21 = lRot128low(krA, krB, 15); + _k20 = lRot128high(kaA, kaB, 15); + _k19 = lRot128low(kaA, kaB, 15); + _k18 = lRot128high(kbA, kbB, 30); + _k17 = lRot128low(kbA, kbB, 30); + _k16 = lRot128high(klA, klB, 45); + _k15 = lRot128low(klA, klB, 45); + _k14 = lRot128high(kaA, kaB, 45); + _k13 = lRot128low(kaA, kaB, 45); + _k12 = lRot128high(krA, krB, 60); + _k11 = lRot128low(krA, krB, 60); + _k10 = lRot128high(kbA, kbB, 60); + _k9 = lRot128low(kbA, kbB, 60); + _k8 = lRot128high(klA, klB, 77); + _k7 = lRot128low(klA, klB, 77); + _k6 = lRot128high(krA, krB, 94); + _k5 = lRot128low(krA, krB, 94); + _k4 = lRot128high(kaA, kaB, 94); + _k3 = lRot128low(kaA, kaB, 94); + _k2 = lRot128high(klA, klB, 111); + _k1 = lRot128low(klA, klB, 111); + _ke6 = lRot128high(krA, krB, 30); + _ke5 = lRot128low(krA, krB, 30); + _ke4 = lRot128high(klA, klB, 60); + _ke3 = lRot128low(klA, klB, 60); + _ke2 = lRot128high(kaA, kaB, 77); + _ke1 = lRot128low(kaA, kaB, 77); + } + } + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("only simple KeyParameter expected."); + + setKey(forEncryption, ((KeyParameter)parameters).GetKey()); + + initialised = true; + } + + public string AlgorithmName + { + get { return "Camellia"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException("Camellia engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (_keyIs128) + { + return processBlock128(input, inOff, output, outOff); + } + else + { + return processBlock192or256(input, inOff, output, outOff); + } + } + + public void Reset() + { + // nothing + } + + private byte lRot8( + byte value, + int rotation) + { +// return (byte)((value << rotation) | ((value & 0xff) >>> (8 - rotation))); + return (byte)((value << rotation) | ((value & 0xff) >> (8 - rotation))); + } + + private int lRot32( + int value, + int rotation) + { + uint uv = (uint) value; +// return (value << rotation) | (value >>> (32 - rotation)); + return (int)((uv << rotation) | (uv >> (32 - rotation))); + } + + private long lRot128high( + long a, + long b, + int rotation) + { + ulong ua = (ulong) a, ub = (ulong) b; + + if (rotation < 64) + { +// a = (a << rotation) | (b >>> (64 - rotation)); + ua = (ua << rotation) | (ub >> (64 - rotation)); + } + else if (rotation == 64) + { + ua = ub; + } + else + { +// a = (b << (rotation - 64)) | (a >>> (64 - (rotation - 64))); + ua = (ub << (rotation - 64)) | (ua >> (64 - (rotation - 64))); + } + +// return a; + return (long) ua; + } + + private long lRot128low( + long a, + long b, + int rotation) + { + ulong ua = (ulong) a, ub = (ulong) b; + + if (rotation < 64) + { +// b = (b << rotation) | (a >>> (64 - rotation)); + ub = (ub << rotation) | (ua >> (64 - rotation)); + } + else if (rotation == 64) + { + ub = ua; + } + else + { +// b = (a << (rotation - 64)) | (b >>> (64 - (rotation - 64))); + ub = (ua << (rotation - 64)) | (ub >> (64 - (rotation - 64))); + } + +// return b; + return (long) ub; + } + + private long fl( + long input, + long ke) + { + int x1 = (int)(input >> 32); + int x2 = (int)input; + int k1 = (int)(ke >> 32); + int k2 = (int)ke; + + x2 = x2 ^ lRot32((x1 & k1), 1); + x1 = x1 ^ (x2 | k2); + + return ((long)x1 << 32) | (x2 & MASK32); + } + + private long flInv( + long input, + long ke) + { + int y1 = (int)(input >> 32); + int y2 = (int)input; + int k1 = (int)(ke >> 32); + int k2 = (int)ke; + + y1 = y1 ^ (y2 | k2); + y2 = y2 ^ lRot32((y1 & k1), 1); + + return ((long)y1 << 32) | (y2 & MASK32); + } + + private long f( + long input, + long ke) + { + long x; + int a, b; + int t1, t2, t3, t4, t5, t6, t7, t8; + int y1, y2, y3, y4, y5, y6, y7, y8; + + x = input ^ ke; + + a = (int)(x >> 32); + b = (int)x; + + t1 = SBOX1[(a >> 24) & 0xff]; + t2 = SBOX2[(a >> 16) & 0xff]; + t3 = SBOX3[(a >> 8) & 0xff]; + t4 = SBOX4[a & 0xff]; + t5 = SBOX2[(b >> 24) & 0xff]; + t6 = SBOX3[(b >> 16) & 0xff]; + t7 = SBOX4[(b >> 8) & 0xff]; + t8 = SBOX1[b & 0xff]; + + y1 = (t1 ^ t3 ^ t4 ^ t6 ^ t7 ^ t8); + y2 = (t1 ^ t2 ^ t4 ^ t5 ^ t7 ^ t8); + y3 = (t1 ^ t2 ^ t3 ^ t5 ^ t6 ^ t8); + y4 = (t2 ^ t3 ^ t4 ^ t5 ^ t6 ^ t7); + y5 = (t1 ^ t2 ^ t6 ^ t7 ^ t8); + y6 = (t2 ^ t3 ^ t5 ^ t7 ^ t8); + y7 = (t3 ^ t4 ^ t5 ^ t6 ^ t8); + y8 = (t1 ^ t4 ^ t5 ^ t6 ^ t7); + + return ((long)y1 << 56) | (((long)y2 & MASK8) << 48) | (((long)y3 & MASK8) << 40) + | (((long)y4 & MASK8) << 32) | (((long)y5 & MASK8) << 24) | (((long)y6 & MASK8) << 16) + | (((long)y7 & MASK8) << 8) | ((long)y8 & MASK8); + } + + private long bytesToWord( + byte[] src, + int srcOff) + { + long word = 0; + + for (int i = 0; i < 8; i++) + { + word = (word << 8) + (src[i + srcOff] & 0xff); + } + + return word; + } + + private void wordToBytes( + long word, + byte[] dst, + int dstOff) + { + ulong uw = (ulong) word; + for (int i = 0; i < 8; i++) + { +// dst[(7 - i) + dstOff] = (byte)word; + dst[(7 - i) + dstOff] = (byte)uw; +// word >>>= 8; + uw >>= 8; + } + } + + private int processBlock128( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + long d1 = bytesToWord(inBytes, inOff); + long d2 = bytesToWord(inBytes, inOff + 8); + + d1 = d1 ^ _kw1; // Prewhitening + d2 = d2 ^ _kw2; + + d2 = d2 ^ f(d1, _k1); // Round 1 + d1 = d1 ^ f(d2, _k2); // Round 2 + d2 = d2 ^ f(d1, _k3); // Round 3 + d1 = d1 ^ f(d2, _k4); // Round 4 + d2 = d2 ^ f(d1, _k5); // Round 5 + d1 = d1 ^ f(d2, _k6); // Round 6 + d1 = fl (d1, _ke1); // FL + d2 = flInv(d2, _ke2); // FLINV + d2 = d2 ^ f(d1, _k7); // Round 7 + d1 = d1 ^ f(d2, _k8); // Round 8 + d2 = d2 ^ f(d1, _k9); // Round 9 + d1 = d1 ^ f(d2, _k10); // Round 10 + d2 = d2 ^ f(d1, _k11); // Round 11 + d1 = d1 ^ f(d2, _k12); // Round 12 + d1 = fl (d1, _ke3); // FL + d2 = flInv(d2, _ke4); // FLINV + + d2 = d2 ^ f(d1, _k13); // Round 13 + d1 = d1 ^ f(d2, _k14); // Round 14 + d2 = d2 ^ f(d1, _k15); // Round 15 + d1 = d1 ^ f(d2, _k16); // Round 16 + d2 = d2 ^ f(d1, _k17); // Round 17 + d1 = d1 ^ f(d2, _k18); // Round 18 + + d2 = d2 ^ _kw3; // Postwhitening + d1 = d1 ^ _kw4; + + wordToBytes(d2, outBytes, outOff); + wordToBytes(d1, outBytes, outOff + 8); + + return BLOCK_SIZE; + } + + private int processBlock192or256( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + long d1 = bytesToWord(inBytes, inOff); + long d2 = bytesToWord(inBytes, inOff + 8); + + d1 = d1 ^ _kw1; // Prewhitening + d2 = d2 ^ _kw2; + + d2 = d2 ^ f(d1, _k1); // Round 1 + d1 = d1 ^ f(d2, _k2); // Round 2 + d2 = d2 ^ f(d1, _k3); // Round 3 + d1 = d1 ^ f(d2, _k4); // Round 4 + d2 = d2 ^ f(d1, _k5); // Round 5 + d1 = d1 ^ f(d2, _k6); // Round 6 + d1 = fl (d1, _ke1); // FL + d2 = flInv(d2, _ke2); // FLINV + d2 = d2 ^ f(d1, _k7); // Round 7 + d1 = d1 ^ f(d2, _k8); // Round 8 + d2 = d2 ^ f(d1, _k9); // Round 9 + d1 = d1 ^ f(d2, _k10); // Round 10 + d2 = d2 ^ f(d1, _k11); // Round 11 + d1 = d1 ^ f(d2, _k12); // Round 12 + d1 = fl (d1, _ke3); // FL + d2 = flInv(d2, _ke4); // FLINV + d2 = d2 ^ f(d1, _k13); // Round 13 + d1 = d1 ^ f(d2, _k14); // Round 14 + d2 = d2 ^ f(d1, _k15); // Round 15 + d1 = d1 ^ f(d2, _k16); // Round 16 + d2 = d2 ^ f(d1, _k17); // Round 17 + d1 = d1 ^ f(d2, _k18); // Round 18 + d1 = fl (d1, _ke5); // FL + d2 = flInv(d2, _ke6); // FLINV + d2 = d2 ^ f(d1, _k19); // Round 19 + d1 = d1 ^ f(d2, _k20); // Round 20 + d2 = d2 ^ f(d1, _k21); // Round 21 + d1 = d1 ^ f(d2, _k22); // Round 22 + d2 = d2 ^ f(d1, _k23); // Round 23 + d1 = d1 ^ f(d2, _k24); // Round 24 + + d2 = d2 ^ _kw3; // Postwhitening + d1 = d1 ^ _kw4; + + wordToBytes(d2, outBytes, outOff); + wordToBytes(d1, outBytes, outOff + 8); + + return BLOCK_SIZE; + } + } +} diff --git a/src/core/srcbc/crypto/engines/CamelliaWrapEngine.cs b/src/core/srcbc/crypto/engines/CamelliaWrapEngine.cs new file mode 100644 index 0000000..26e7602 --- /dev/null +++ b/src/core/srcbc/crypto/engines/CamelliaWrapEngine.cs @@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Engines +{ + /// + /// An implementation of the Camellia key wrapper based on RFC 3657/RFC 3394. + ///

+ /// For further details see: http://www.ietf.org/rfc/rfc3657.txt. + /// + public class CamelliaWrapEngine + : Rfc3394WrapEngine + { + public CamelliaWrapEngine() + : base(new CamelliaEngine()) + { + } + } +} diff --git a/src/core/srcbc/crypto/engines/Cast5Engine.cs b/src/core/srcbc/crypto/engines/Cast5Engine.cs new file mode 100644 index 0000000..56743b6 --- /dev/null +++ b/src/core/srcbc/crypto/engines/Cast5Engine.cs @@ -0,0 +1,829 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * A class that provides CAST key encryption operations, + * such as encoding data and generating keys. + * + * All the algorithms herein are from the Internet RFC's + * + * RFC2144 - Cast5 (64bit block, 40-128bit key) + * RFC2612 - CAST6 (128bit block, 128-256bit key) + * + * and implement a simplified cryptography interface. + */ + public class Cast5Engine + : IBlockCipher + { + internal const int M32 = unchecked((int) 0xffffffff); + + internal static readonly int[] + S1 = { + unchecked((int) 0x30fb40d4), unchecked((int) 0x9fa0ff0b), unchecked((int) 0x6beccd2f), unchecked((int) 0x3f258c7a), unchecked((int) 0x1e213f2f), unchecked((int) 0x9c004dd3), unchecked((int) 0x6003e540), unchecked((int) 0xcf9fc949), + unchecked((int) 0xbfd4af27), unchecked((int) 0x88bbbdb5), unchecked((int) 0xe2034090), unchecked((int) 0x98d09675), unchecked((int) 0x6e63a0e0), unchecked((int) 0x15c361d2), unchecked((int) 0xc2e7661d), unchecked((int) 0x22d4ff8e), + unchecked((int) 0x28683b6f), unchecked((int) 0xc07fd059), unchecked((int) 0xff2379c8), unchecked((int) 0x775f50e2), unchecked((int) 0x43c340d3), unchecked((int) 0xdf2f8656), unchecked((int) 0x887ca41a), unchecked((int) 0xa2d2bd2d), + unchecked((int) 0xa1c9e0d6), unchecked((int) 0x346c4819), unchecked((int) 0x61b76d87), unchecked((int) 0x22540f2f), unchecked((int) 0x2abe32e1), unchecked((int) 0xaa54166b), unchecked((int) 0x22568e3a), unchecked((int) 0xa2d341d0), + unchecked((int) 0x66db40c8), unchecked((int) 0xa784392f), unchecked((int) 0x004dff2f), unchecked((int) 0x2db9d2de), unchecked((int) 0x97943fac), unchecked((int) 0x4a97c1d8), unchecked((int) 0x527644b7), unchecked((int) 0xb5f437a7), + unchecked((int) 0xb82cbaef), unchecked((int) 0xd751d159), unchecked((int) 0x6ff7f0ed), unchecked((int) 0x5a097a1f), unchecked((int) 0x827b68d0), unchecked((int) 0x90ecf52e), unchecked((int) 0x22b0c054), unchecked((int) 0xbc8e5935), + unchecked((int) 0x4b6d2f7f), unchecked((int) 0x50bb64a2), unchecked((int) 0xd2664910), unchecked((int) 0xbee5812d), unchecked((int) 0xb7332290), unchecked((int) 0xe93b159f), unchecked((int) 0xb48ee411), unchecked((int) 0x4bff345d), + unchecked((int) 0xfd45c240), unchecked((int) 0xad31973f), unchecked((int) 0xc4f6d02e), unchecked((int) 0x55fc8165), unchecked((int) 0xd5b1caad), unchecked((int) 0xa1ac2dae), unchecked((int) 0xa2d4b76d), unchecked((int) 0xc19b0c50), + unchecked((int) 0x882240f2), unchecked((int) 0x0c6e4f38), unchecked((int) 0xa4e4bfd7), unchecked((int) 0x4f5ba272), unchecked((int) 0x564c1d2f), unchecked((int) 0xc59c5319), unchecked((int) 0xb949e354), unchecked((int) 0xb04669fe), + unchecked((int) 0xb1b6ab8a), unchecked((int) 0xc71358dd), unchecked((int) 0x6385c545), unchecked((int) 0x110f935d), unchecked((int) 0x57538ad5), unchecked((int) 0x6a390493), unchecked((int) 0xe63d37e0), unchecked((int) 0x2a54f6b3), + unchecked((int) 0x3a787d5f), unchecked((int) 0x6276a0b5), unchecked((int) 0x19a6fcdf), unchecked((int) 0x7a42206a), unchecked((int) 0x29f9d4d5), unchecked((int) 0xf61b1891), unchecked((int) 0xbb72275e), unchecked((int) 0xaa508167), + unchecked((int) 0x38901091), unchecked((int) 0xc6b505eb), unchecked((int) 0x84c7cb8c), unchecked((int) 0x2ad75a0f), unchecked((int) 0x874a1427), unchecked((int) 0xa2d1936b), unchecked((int) 0x2ad286af), unchecked((int) 0xaa56d291), + unchecked((int) 0xd7894360), unchecked((int) 0x425c750d), unchecked((int) 0x93b39e26), unchecked((int) 0x187184c9), unchecked((int) 0x6c00b32d), unchecked((int) 0x73e2bb14), unchecked((int) 0xa0bebc3c), unchecked((int) 0x54623779), + unchecked((int) 0x64459eab), unchecked((int) 0x3f328b82), unchecked((int) 0x7718cf82), unchecked((int) 0x59a2cea6), unchecked((int) 0x04ee002e), unchecked((int) 0x89fe78e6), unchecked((int) 0x3fab0950), unchecked((int) 0x325ff6c2), + unchecked((int) 0x81383f05), unchecked((int) 0x6963c5c8), unchecked((int) 0x76cb5ad6), unchecked((int) 0xd49974c9), unchecked((int) 0xca180dcf), unchecked((int) 0x380782d5), unchecked((int) 0xc7fa5cf6), unchecked((int) 0x8ac31511), + unchecked((int) 0x35e79e13), unchecked((int) 0x47da91d0), unchecked((int) 0xf40f9086), unchecked((int) 0xa7e2419e), unchecked((int) 0x31366241), unchecked((int) 0x051ef495), unchecked((int) 0xaa573b04), unchecked((int) 0x4a805d8d), + unchecked((int) 0x548300d0), unchecked((int) 0x00322a3c), unchecked((int) 0xbf64cddf), unchecked((int) 0xba57a68e), unchecked((int) 0x75c6372b), unchecked((int) 0x50afd341), unchecked((int) 0xa7c13275), unchecked((int) 0x915a0bf5), + unchecked((int) 0x6b54bfab), unchecked((int) 0x2b0b1426), unchecked((int) 0xab4cc9d7), unchecked((int) 0x449ccd82), unchecked((int) 0xf7fbf265), unchecked((int) 0xab85c5f3), unchecked((int) 0x1b55db94), unchecked((int) 0xaad4e324), + unchecked((int) 0xcfa4bd3f), unchecked((int) 0x2deaa3e2), unchecked((int) 0x9e204d02), unchecked((int) 0xc8bd25ac), unchecked((int) 0xeadf55b3), unchecked((int) 0xd5bd9e98), unchecked((int) 0xe31231b2), unchecked((int) 0x2ad5ad6c), + unchecked((int) 0x954329de), unchecked((int) 0xadbe4528), unchecked((int) 0xd8710f69), unchecked((int) 0xaa51c90f), unchecked((int) 0xaa786bf6), unchecked((int) 0x22513f1e), unchecked((int) 0xaa51a79b), unchecked((int) 0x2ad344cc), + unchecked((int) 0x7b5a41f0), unchecked((int) 0xd37cfbad), unchecked((int) 0x1b069505), unchecked((int) 0x41ece491), unchecked((int) 0xb4c332e6), unchecked((int) 0x032268d4), unchecked((int) 0xc9600acc), unchecked((int) 0xce387e6d), + unchecked((int) 0xbf6bb16c), unchecked((int) 0x6a70fb78), unchecked((int) 0x0d03d9c9), unchecked((int) 0xd4df39de), unchecked((int) 0xe01063da), unchecked((int) 0x4736f464), unchecked((int) 0x5ad328d8), unchecked((int) 0xb347cc96), + unchecked((int) 0x75bb0fc3), unchecked((int) 0x98511bfb), unchecked((int) 0x4ffbcc35), unchecked((int) 0xb58bcf6a), unchecked((int) 0xe11f0abc), unchecked((int) 0xbfc5fe4a), unchecked((int) 0xa70aec10), unchecked((int) 0xac39570a), + unchecked((int) 0x3f04442f), unchecked((int) 0x6188b153), unchecked((int) 0xe0397a2e), unchecked((int) 0x5727cb79), unchecked((int) 0x9ceb418f), unchecked((int) 0x1cacd68d), unchecked((int) 0x2ad37c96), unchecked((int) 0x0175cb9d), + unchecked((int) 0xc69dff09), unchecked((int) 0xc75b65f0), unchecked((int) 0xd9db40d8), unchecked((int) 0xec0e7779), unchecked((int) 0x4744ead4), unchecked((int) 0xb11c3274), unchecked((int) 0xdd24cb9e), unchecked((int) 0x7e1c54bd), + unchecked((int) 0xf01144f9), unchecked((int) 0xd2240eb1), unchecked((int) 0x9675b3fd), unchecked((int) 0xa3ac3755), unchecked((int) 0xd47c27af), unchecked((int) 0x51c85f4d), unchecked((int) 0x56907596), unchecked((int) 0xa5bb15e6), + unchecked((int) 0x580304f0), unchecked((int) 0xca042cf1), unchecked((int) 0x011a37ea), unchecked((int) 0x8dbfaadb), unchecked((int) 0x35ba3e4a), unchecked((int) 0x3526ffa0), unchecked((int) 0xc37b4d09), unchecked((int) 0xbc306ed9), + unchecked((int) 0x98a52666), unchecked((int) 0x5648f725), unchecked((int) 0xff5e569d), unchecked((int) 0x0ced63d0), unchecked((int) 0x7c63b2cf), unchecked((int) 0x700b45e1), unchecked((int) 0xd5ea50f1), unchecked((int) 0x85a92872), + unchecked((int) 0xaf1fbda7), unchecked((int) 0xd4234870), unchecked((int) 0xa7870bf3), unchecked((int) 0x2d3b4d79), unchecked((int) 0x42e04198), unchecked((int) 0x0cd0ede7), unchecked((int) 0x26470db8), unchecked((int) 0xf881814c), + unchecked((int) 0x474d6ad7), unchecked((int) 0x7c0c5e5c), unchecked((int) 0xd1231959), unchecked((int) 0x381b7298), unchecked((int) 0xf5d2f4db), unchecked((int) 0xab838653), unchecked((int) 0x6e2f1e23), unchecked((int) 0x83719c9e), + unchecked((int) 0xbd91e046), unchecked((int) 0x9a56456e), unchecked((int) 0xdc39200c), unchecked((int) 0x20c8c571), unchecked((int) 0x962bda1c), unchecked((int) 0xe1e696ff), unchecked((int) 0xb141ab08), unchecked((int) 0x7cca89b9), + unchecked((int) 0x1a69e783), unchecked((int) 0x02cc4843), unchecked((int) 0xa2f7c579), unchecked((int) 0x429ef47d), unchecked((int) 0x427b169c), unchecked((int) 0x5ac9f049), unchecked((int) 0xdd8f0f00), unchecked((int) 0x5c8165bf) + }, + S2 = + { + unchecked((int) 0x1f201094), unchecked((int) 0xef0ba75b), unchecked((int) 0x69e3cf7e), unchecked((int) 0x393f4380), unchecked((int) 0xfe61cf7a), unchecked((int) 0xeec5207a), unchecked((int) 0x55889c94), unchecked((int) 0x72fc0651), + unchecked((int) 0xada7ef79), unchecked((int) 0x4e1d7235), unchecked((int) 0xd55a63ce), unchecked((int) 0xde0436ba), unchecked((int) 0x99c430ef), unchecked((int) 0x5f0c0794), unchecked((int) 0x18dcdb7d), unchecked((int) 0xa1d6eff3), + unchecked((int) 0xa0b52f7b), unchecked((int) 0x59e83605), unchecked((int) 0xee15b094), unchecked((int) 0xe9ffd909), unchecked((int) 0xdc440086), unchecked((int) 0xef944459), unchecked((int) 0xba83ccb3), unchecked((int) 0xe0c3cdfb), + unchecked((int) 0xd1da4181), unchecked((int) 0x3b092ab1), unchecked((int) 0xf997f1c1), unchecked((int) 0xa5e6cf7b), unchecked((int) 0x01420ddb), unchecked((int) 0xe4e7ef5b), unchecked((int) 0x25a1ff41), unchecked((int) 0xe180f806), + unchecked((int) 0x1fc41080), unchecked((int) 0x179bee7a), unchecked((int) 0xd37ac6a9), unchecked((int) 0xfe5830a4), unchecked((int) 0x98de8b7f), unchecked((int) 0x77e83f4e), unchecked((int) 0x79929269), unchecked((int) 0x24fa9f7b), + unchecked((int) 0xe113c85b), unchecked((int) 0xacc40083), unchecked((int) 0xd7503525), unchecked((int) 0xf7ea615f), unchecked((int) 0x62143154), unchecked((int) 0x0d554b63), unchecked((int) 0x5d681121), unchecked((int) 0xc866c359), + unchecked((int) 0x3d63cf73), unchecked((int) 0xcee234c0), unchecked((int) 0xd4d87e87), unchecked((int) 0x5c672b21), unchecked((int) 0x071f6181), unchecked((int) 0x39f7627f), unchecked((int) 0x361e3084), unchecked((int) 0xe4eb573b), + unchecked((int) 0x602f64a4), unchecked((int) 0xd63acd9c), unchecked((int) 0x1bbc4635), unchecked((int) 0x9e81032d), unchecked((int) 0x2701f50c), unchecked((int) 0x99847ab4), unchecked((int) 0xa0e3df79), unchecked((int) 0xba6cf38c), + unchecked((int) 0x10843094), unchecked((int) 0x2537a95e), unchecked((int) 0xf46f6ffe), unchecked((int) 0xa1ff3b1f), unchecked((int) 0x208cfb6a), unchecked((int) 0x8f458c74), unchecked((int) 0xd9e0a227), unchecked((int) 0x4ec73a34), + unchecked((int) 0xfc884f69), unchecked((int) 0x3e4de8df), unchecked((int) 0xef0e0088), unchecked((int) 0x3559648d), unchecked((int) 0x8a45388c), unchecked((int) 0x1d804366), unchecked((int) 0x721d9bfd), unchecked((int) 0xa58684bb), + unchecked((int) 0xe8256333), unchecked((int) 0x844e8212), unchecked((int) 0x128d8098), unchecked((int) 0xfed33fb4), unchecked((int) 0xce280ae1), unchecked((int) 0x27e19ba5), unchecked((int) 0xd5a6c252), unchecked((int) 0xe49754bd), + unchecked((int) 0xc5d655dd), unchecked((int) 0xeb667064), unchecked((int) 0x77840b4d), unchecked((int) 0xa1b6a801), unchecked((int) 0x84db26a9), unchecked((int) 0xe0b56714), unchecked((int) 0x21f043b7), unchecked((int) 0xe5d05860), + unchecked((int) 0x54f03084), unchecked((int) 0x066ff472), unchecked((int) 0xa31aa153), unchecked((int) 0xdadc4755), unchecked((int) 0xb5625dbf), unchecked((int) 0x68561be6), unchecked((int) 0x83ca6b94), unchecked((int) 0x2d6ed23b), + unchecked((int) 0xeccf01db), unchecked((int) 0xa6d3d0ba), unchecked((int) 0xb6803d5c), unchecked((int) 0xaf77a709), unchecked((int) 0x33b4a34c), unchecked((int) 0x397bc8d6), unchecked((int) 0x5ee22b95), unchecked((int) 0x5f0e5304), + unchecked((int) 0x81ed6f61), unchecked((int) 0x20e74364), unchecked((int) 0xb45e1378), unchecked((int) 0xde18639b), unchecked((int) 0x881ca122), unchecked((int) 0xb96726d1), unchecked((int) 0x8049a7e8), unchecked((int) 0x22b7da7b), + unchecked((int) 0x5e552d25), unchecked((int) 0x5272d237), unchecked((int) 0x79d2951c), unchecked((int) 0xc60d894c), unchecked((int) 0x488cb402), unchecked((int) 0x1ba4fe5b), unchecked((int) 0xa4b09f6b), unchecked((int) 0x1ca815cf), + unchecked((int) 0xa20c3005), unchecked((int) 0x8871df63), unchecked((int) 0xb9de2fcb), unchecked((int) 0x0cc6c9e9), unchecked((int) 0x0beeff53), unchecked((int) 0xe3214517), unchecked((int) 0xb4542835), unchecked((int) 0x9f63293c), + unchecked((int) 0xee41e729), unchecked((int) 0x6e1d2d7c), unchecked((int) 0x50045286), unchecked((int) 0x1e6685f3), unchecked((int) 0xf33401c6), unchecked((int) 0x30a22c95), unchecked((int) 0x31a70850), unchecked((int) 0x60930f13), + unchecked((int) 0x73f98417), unchecked((int) 0xa1269859), unchecked((int) 0xec645c44), unchecked((int) 0x52c877a9), unchecked((int) 0xcdff33a6), unchecked((int) 0xa02b1741), unchecked((int) 0x7cbad9a2), unchecked((int) 0x2180036f), + unchecked((int) 0x50d99c08), unchecked((int) 0xcb3f4861), unchecked((int) 0xc26bd765), unchecked((int) 0x64a3f6ab), unchecked((int) 0x80342676), unchecked((int) 0x25a75e7b), unchecked((int) 0xe4e6d1fc), unchecked((int) 0x20c710e6), + unchecked((int) 0xcdf0b680), unchecked((int) 0x17844d3b), unchecked((int) 0x31eef84d), unchecked((int) 0x7e0824e4), unchecked((int) 0x2ccb49eb), unchecked((int) 0x846a3bae), unchecked((int) 0x8ff77888), unchecked((int) 0xee5d60f6), + unchecked((int) 0x7af75673), unchecked((int) 0x2fdd5cdb), unchecked((int) 0xa11631c1), unchecked((int) 0x30f66f43), unchecked((int) 0xb3faec54), unchecked((int) 0x157fd7fa), unchecked((int) 0xef8579cc), unchecked((int) 0xd152de58), + unchecked((int) 0xdb2ffd5e), unchecked((int) 0x8f32ce19), unchecked((int) 0x306af97a), unchecked((int) 0x02f03ef8), unchecked((int) 0x99319ad5), unchecked((int) 0xc242fa0f), unchecked((int) 0xa7e3ebb0), unchecked((int) 0xc68e4906), + unchecked((int) 0xb8da230c), unchecked((int) 0x80823028), unchecked((int) 0xdcdef3c8), unchecked((int) 0xd35fb171), unchecked((int) 0x088a1bc8), unchecked((int) 0xbec0c560), unchecked((int) 0x61a3c9e8), unchecked((int) 0xbca8f54d), + unchecked((int) 0xc72feffa), unchecked((int) 0x22822e99), unchecked((int) 0x82c570b4), unchecked((int) 0xd8d94e89), unchecked((int) 0x8b1c34bc), unchecked((int) 0x301e16e6), unchecked((int) 0x273be979), unchecked((int) 0xb0ffeaa6), + unchecked((int) 0x61d9b8c6), unchecked((int) 0x00b24869), unchecked((int) 0xb7ffce3f), unchecked((int) 0x08dc283b), unchecked((int) 0x43daf65a), unchecked((int) 0xf7e19798), unchecked((int) 0x7619b72f), unchecked((int) 0x8f1c9ba4), + unchecked((int) 0xdc8637a0), unchecked((int) 0x16a7d3b1), unchecked((int) 0x9fc393b7), unchecked((int) 0xa7136eeb), unchecked((int) 0xc6bcc63e), unchecked((int) 0x1a513742), unchecked((int) 0xef6828bc), unchecked((int) 0x520365d6), + unchecked((int) 0x2d6a77ab), unchecked((int) 0x3527ed4b), unchecked((int) 0x821fd216), unchecked((int) 0x095c6e2e), unchecked((int) 0xdb92f2fb), unchecked((int) 0x5eea29cb), unchecked((int) 0x145892f5), unchecked((int) 0x91584f7f), + unchecked((int) 0x5483697b), unchecked((int) 0x2667a8cc), unchecked((int) 0x85196048), unchecked((int) 0x8c4bacea), unchecked((int) 0x833860d4), unchecked((int) 0x0d23e0f9), unchecked((int) 0x6c387e8a), unchecked((int) 0x0ae6d249), + unchecked((int) 0xb284600c), unchecked((int) 0xd835731d), unchecked((int) 0xdcb1c647), unchecked((int) 0xac4c56ea), unchecked((int) 0x3ebd81b3), unchecked((int) 0x230eabb0), unchecked((int) 0x6438bc87), unchecked((int) 0xf0b5b1fa), + unchecked((int) 0x8f5ea2b3), unchecked((int) 0xfc184642), unchecked((int) 0x0a036b7a), unchecked((int) 0x4fb089bd), unchecked((int) 0x649da589), unchecked((int) 0xa345415e), unchecked((int) 0x5c038323), unchecked((int) 0x3e5d3bb9), + unchecked((int) 0x43d79572), unchecked((int) 0x7e6dd07c), unchecked((int) 0x06dfdf1e), unchecked((int) 0x6c6cc4ef), unchecked((int) 0x7160a539), unchecked((int) 0x73bfbe70), unchecked((int) 0x83877605), unchecked((int) 0x4523ecf1) + }, + S3 = + { + unchecked((int) 0x8defc240), unchecked((int) 0x25fa5d9f), unchecked((int) 0xeb903dbf), unchecked((int) 0xe810c907), unchecked((int) 0x47607fff), unchecked((int) 0x369fe44b), unchecked((int) 0x8c1fc644), unchecked((int) 0xaececa90), + unchecked((int) 0xbeb1f9bf), unchecked((int) 0xeefbcaea), unchecked((int) 0xe8cf1950), unchecked((int) 0x51df07ae), unchecked((int) 0x920e8806), unchecked((int) 0xf0ad0548), unchecked((int) 0xe13c8d83), unchecked((int) 0x927010d5), + unchecked((int) 0x11107d9f), unchecked((int) 0x07647db9), unchecked((int) 0xb2e3e4d4), unchecked((int) 0x3d4f285e), unchecked((int) 0xb9afa820), unchecked((int) 0xfade82e0), unchecked((int) 0xa067268b), unchecked((int) 0x8272792e), + unchecked((int) 0x553fb2c0), unchecked((int) 0x489ae22b), unchecked((int) 0xd4ef9794), unchecked((int) 0x125e3fbc), unchecked((int) 0x21fffcee), unchecked((int) 0x825b1bfd), unchecked((int) 0x9255c5ed), unchecked((int) 0x1257a240), + unchecked((int) 0x4e1a8302), unchecked((int) 0xbae07fff), unchecked((int) 0x528246e7), unchecked((int) 0x8e57140e), unchecked((int) 0x3373f7bf), unchecked((int) 0x8c9f8188), unchecked((int) 0xa6fc4ee8), unchecked((int) 0xc982b5a5), + unchecked((int) 0xa8c01db7), unchecked((int) 0x579fc264), unchecked((int) 0x67094f31), unchecked((int) 0xf2bd3f5f), unchecked((int) 0x40fff7c1), unchecked((int) 0x1fb78dfc), unchecked((int) 0x8e6bd2c1), unchecked((int) 0x437be59b), + unchecked((int) 0x99b03dbf), unchecked((int) 0xb5dbc64b), unchecked((int) 0x638dc0e6), unchecked((int) 0x55819d99), unchecked((int) 0xa197c81c), unchecked((int) 0x4a012d6e), unchecked((int) 0xc5884a28), unchecked((int) 0xccc36f71), + unchecked((int) 0xb843c213), unchecked((int) 0x6c0743f1), unchecked((int) 0x8309893c), unchecked((int) 0x0feddd5f), unchecked((int) 0x2f7fe850), unchecked((int) 0xd7c07f7e), unchecked((int) 0x02507fbf), unchecked((int) 0x5afb9a04), + unchecked((int) 0xa747d2d0), unchecked((int) 0x1651192e), unchecked((int) 0xaf70bf3e), unchecked((int) 0x58c31380), unchecked((int) 0x5f98302e), unchecked((int) 0x727cc3c4), unchecked((int) 0x0a0fb402), unchecked((int) 0x0f7fef82), + unchecked((int) 0x8c96fdad), unchecked((int) 0x5d2c2aae), unchecked((int) 0x8ee99a49), unchecked((int) 0x50da88b8), unchecked((int) 0x8427f4a0), unchecked((int) 0x1eac5790), unchecked((int) 0x796fb449), unchecked((int) 0x8252dc15), + unchecked((int) 0xefbd7d9b), unchecked((int) 0xa672597d), unchecked((int) 0xada840d8), unchecked((int) 0x45f54504), unchecked((int) 0xfa5d7403), unchecked((int) 0xe83ec305), unchecked((int) 0x4f91751a), unchecked((int) 0x925669c2), + unchecked((int) 0x23efe941), unchecked((int) 0xa903f12e), unchecked((int) 0x60270df2), unchecked((int) 0x0276e4b6), unchecked((int) 0x94fd6574), unchecked((int) 0x927985b2), unchecked((int) 0x8276dbcb), unchecked((int) 0x02778176), + unchecked((int) 0xf8af918d), unchecked((int) 0x4e48f79e), unchecked((int) 0x8f616ddf), unchecked((int) 0xe29d840e), unchecked((int) 0x842f7d83), unchecked((int) 0x340ce5c8), unchecked((int) 0x96bbb682), unchecked((int) 0x93b4b148), + unchecked((int) 0xef303cab), unchecked((int) 0x984faf28), unchecked((int) 0x779faf9b), unchecked((int) 0x92dc560d), unchecked((int) 0x224d1e20), unchecked((int) 0x8437aa88), unchecked((int) 0x7d29dc96), unchecked((int) 0x2756d3dc), + unchecked((int) 0x8b907cee), unchecked((int) 0xb51fd240), unchecked((int) 0xe7c07ce3), unchecked((int) 0xe566b4a1), unchecked((int) 0xc3e9615e), unchecked((int) 0x3cf8209d), unchecked((int) 0x6094d1e3), unchecked((int) 0xcd9ca341), + unchecked((int) 0x5c76460e), unchecked((int) 0x00ea983b), unchecked((int) 0xd4d67881), unchecked((int) 0xfd47572c), unchecked((int) 0xf76cedd9), unchecked((int) 0xbda8229c), unchecked((int) 0x127dadaa), unchecked((int) 0x438a074e), + unchecked((int) 0x1f97c090), unchecked((int) 0x081bdb8a), unchecked((int) 0x93a07ebe), unchecked((int) 0xb938ca15), unchecked((int) 0x97b03cff), unchecked((int) 0x3dc2c0f8), unchecked((int) 0x8d1ab2ec), unchecked((int) 0x64380e51), + unchecked((int) 0x68cc7bfb), unchecked((int) 0xd90f2788), unchecked((int) 0x12490181), unchecked((int) 0x5de5ffd4), unchecked((int) 0xdd7ef86a), unchecked((int) 0x76a2e214), unchecked((int) 0xb9a40368), unchecked((int) 0x925d958f), + unchecked((int) 0x4b39fffa), unchecked((int) 0xba39aee9), unchecked((int) 0xa4ffd30b), unchecked((int) 0xfaf7933b), unchecked((int) 0x6d498623), unchecked((int) 0x193cbcfa), unchecked((int) 0x27627545), unchecked((int) 0x825cf47a), + unchecked((int) 0x61bd8ba0), unchecked((int) 0xd11e42d1), unchecked((int) 0xcead04f4), unchecked((int) 0x127ea392), unchecked((int) 0x10428db7), unchecked((int) 0x8272a972), unchecked((int) 0x9270c4a8), unchecked((int) 0x127de50b), + unchecked((int) 0x285ba1c8), unchecked((int) 0x3c62f44f), unchecked((int) 0x35c0eaa5), unchecked((int) 0xe805d231), unchecked((int) 0x428929fb), unchecked((int) 0xb4fcdf82), unchecked((int) 0x4fb66a53), unchecked((int) 0x0e7dc15b), + unchecked((int) 0x1f081fab), unchecked((int) 0x108618ae), unchecked((int) 0xfcfd086d), unchecked((int) 0xf9ff2889), unchecked((int) 0x694bcc11), unchecked((int) 0x236a5cae), unchecked((int) 0x12deca4d), unchecked((int) 0x2c3f8cc5), + unchecked((int) 0xd2d02dfe), unchecked((int) 0xf8ef5896), unchecked((int) 0xe4cf52da), unchecked((int) 0x95155b67), unchecked((int) 0x494a488c), unchecked((int) 0xb9b6a80c), unchecked((int) 0x5c8f82bc), unchecked((int) 0x89d36b45), + unchecked((int) 0x3a609437), unchecked((int) 0xec00c9a9), unchecked((int) 0x44715253), unchecked((int) 0x0a874b49), unchecked((int) 0xd773bc40), unchecked((int) 0x7c34671c), unchecked((int) 0x02717ef6), unchecked((int) 0x4feb5536), + unchecked((int) 0xa2d02fff), unchecked((int) 0xd2bf60c4), unchecked((int) 0xd43f03c0), unchecked((int) 0x50b4ef6d), unchecked((int) 0x07478cd1), unchecked((int) 0x006e1888), unchecked((int) 0xa2e53f55), unchecked((int) 0xb9e6d4bc), + unchecked((int) 0xa2048016), unchecked((int) 0x97573833), unchecked((int) 0xd7207d67), unchecked((int) 0xde0f8f3d), unchecked((int) 0x72f87b33), unchecked((int) 0xabcc4f33), unchecked((int) 0x7688c55d), unchecked((int) 0x7b00a6b0), + unchecked((int) 0x947b0001), unchecked((int) 0x570075d2), unchecked((int) 0xf9bb88f8), unchecked((int) 0x8942019e), unchecked((int) 0x4264a5ff), unchecked((int) 0x856302e0), unchecked((int) 0x72dbd92b), unchecked((int) 0xee971b69), + unchecked((int) 0x6ea22fde), unchecked((int) 0x5f08ae2b), unchecked((int) 0xaf7a616d), unchecked((int) 0xe5c98767), unchecked((int) 0xcf1febd2), unchecked((int) 0x61efc8c2), unchecked((int) 0xf1ac2571), unchecked((int) 0xcc8239c2), + unchecked((int) 0x67214cb8), unchecked((int) 0xb1e583d1), unchecked((int) 0xb7dc3e62), unchecked((int) 0x7f10bdce), unchecked((int) 0xf90a5c38), unchecked((int) 0x0ff0443d), unchecked((int) 0x606e6dc6), unchecked((int) 0x60543a49), + unchecked((int) 0x5727c148), unchecked((int) 0x2be98a1d), unchecked((int) 0x8ab41738), unchecked((int) 0x20e1be24), unchecked((int) 0xaf96da0f), unchecked((int) 0x68458425), unchecked((int) 0x99833be5), unchecked((int) 0x600d457d), + unchecked((int) 0x282f9350), unchecked((int) 0x8334b362), unchecked((int) 0xd91d1120), unchecked((int) 0x2b6d8da0), unchecked((int) 0x642b1e31), unchecked((int) 0x9c305a00), unchecked((int) 0x52bce688), unchecked((int) 0x1b03588a), + unchecked((int) 0xf7baefd5), unchecked((int) 0x4142ed9c), unchecked((int) 0xa4315c11), unchecked((int) 0x83323ec5), unchecked((int) 0xdfef4636), unchecked((int) 0xa133c501), unchecked((int) 0xe9d3531c), unchecked((int) 0xee353783) + }, + S4 = + { + unchecked((int) 0x9db30420), unchecked((int) 0x1fb6e9de), unchecked((int) 0xa7be7bef), unchecked((int) 0xd273a298), unchecked((int) 0x4a4f7bdb), unchecked((int) 0x64ad8c57), unchecked((int) 0x85510443), unchecked((int) 0xfa020ed1), + unchecked((int) 0x7e287aff), unchecked((int) 0xe60fb663), unchecked((int) 0x095f35a1), unchecked((int) 0x79ebf120), unchecked((int) 0xfd059d43), unchecked((int) 0x6497b7b1), unchecked((int) 0xf3641f63), unchecked((int) 0x241e4adf), + unchecked((int) 0x28147f5f), unchecked((int) 0x4fa2b8cd), unchecked((int) 0xc9430040), unchecked((int) 0x0cc32220), unchecked((int) 0xfdd30b30), unchecked((int) 0xc0a5374f), unchecked((int) 0x1d2d00d9), unchecked((int) 0x24147b15), + unchecked((int) 0xee4d111a), unchecked((int) 0x0fca5167), unchecked((int) 0x71ff904c), unchecked((int) 0x2d195ffe), unchecked((int) 0x1a05645f), unchecked((int) 0x0c13fefe), unchecked((int) 0x081b08ca), unchecked((int) 0x05170121), + unchecked((int) 0x80530100), unchecked((int) 0xe83e5efe), unchecked((int) 0xac9af4f8), unchecked((int) 0x7fe72701), unchecked((int) 0xd2b8ee5f), unchecked((int) 0x06df4261), unchecked((int) 0xbb9e9b8a), unchecked((int) 0x7293ea25), + unchecked((int) 0xce84ffdf), unchecked((int) 0xf5718801), unchecked((int) 0x3dd64b04), unchecked((int) 0xa26f263b), unchecked((int) 0x7ed48400), unchecked((int) 0x547eebe6), unchecked((int) 0x446d4ca0), unchecked((int) 0x6cf3d6f5), + unchecked((int) 0x2649abdf), unchecked((int) 0xaea0c7f5), unchecked((int) 0x36338cc1), unchecked((int) 0x503f7e93), unchecked((int) 0xd3772061), unchecked((int) 0x11b638e1), unchecked((int) 0x72500e03), unchecked((int) 0xf80eb2bb), + unchecked((int) 0xabe0502e), unchecked((int) 0xec8d77de), unchecked((int) 0x57971e81), unchecked((int) 0xe14f6746), unchecked((int) 0xc9335400), unchecked((int) 0x6920318f), unchecked((int) 0x081dbb99), unchecked((int) 0xffc304a5), + unchecked((int) 0x4d351805), unchecked((int) 0x7f3d5ce3), unchecked((int) 0xa6c866c6), unchecked((int) 0x5d5bcca9), unchecked((int) 0xdaec6fea), unchecked((int) 0x9f926f91), unchecked((int) 0x9f46222f), unchecked((int) 0x3991467d), + unchecked((int) 0xa5bf6d8e), unchecked((int) 0x1143c44f), unchecked((int) 0x43958302), unchecked((int) 0xd0214eeb), unchecked((int) 0x022083b8), unchecked((int) 0x3fb6180c), unchecked((int) 0x18f8931e), unchecked((int) 0x281658e6), + unchecked((int) 0x26486e3e), unchecked((int) 0x8bd78a70), unchecked((int) 0x7477e4c1), unchecked((int) 0xb506e07c), unchecked((int) 0xf32d0a25), unchecked((int) 0x79098b02), unchecked((int) 0xe4eabb81), unchecked((int) 0x28123b23), + unchecked((int) 0x69dead38), unchecked((int) 0x1574ca16), unchecked((int) 0xdf871b62), unchecked((int) 0x211c40b7), unchecked((int) 0xa51a9ef9), unchecked((int) 0x0014377b), unchecked((int) 0x041e8ac8), unchecked((int) 0x09114003), + unchecked((int) 0xbd59e4d2), unchecked((int) 0xe3d156d5), unchecked((int) 0x4fe876d5), unchecked((int) 0x2f91a340), unchecked((int) 0x557be8de), unchecked((int) 0x00eae4a7), unchecked((int) 0x0ce5c2ec), unchecked((int) 0x4db4bba6), + unchecked((int) 0xe756bdff), unchecked((int) 0xdd3369ac), unchecked((int) 0xec17b035), unchecked((int) 0x06572327), unchecked((int) 0x99afc8b0), unchecked((int) 0x56c8c391), unchecked((int) 0x6b65811c), unchecked((int) 0x5e146119), + unchecked((int) 0x6e85cb75), unchecked((int) 0xbe07c002), unchecked((int) 0xc2325577), unchecked((int) 0x893ff4ec), unchecked((int) 0x5bbfc92d), unchecked((int) 0xd0ec3b25), unchecked((int) 0xb7801ab7), unchecked((int) 0x8d6d3b24), + unchecked((int) 0x20c763ef), unchecked((int) 0xc366a5fc), unchecked((int) 0x9c382880), unchecked((int) 0x0ace3205), unchecked((int) 0xaac9548a), unchecked((int) 0xeca1d7c7), unchecked((int) 0x041afa32), unchecked((int) 0x1d16625a), + unchecked((int) 0x6701902c), unchecked((int) 0x9b757a54), unchecked((int) 0x31d477f7), unchecked((int) 0x9126b031), unchecked((int) 0x36cc6fdb), unchecked((int) 0xc70b8b46), unchecked((int) 0xd9e66a48), unchecked((int) 0x56e55a79), + unchecked((int) 0x026a4ceb), unchecked((int) 0x52437eff), unchecked((int) 0x2f8f76b4), unchecked((int) 0x0df980a5), unchecked((int) 0x8674cde3), unchecked((int) 0xedda04eb), unchecked((int) 0x17a9be04), unchecked((int) 0x2c18f4df), + unchecked((int) 0xb7747f9d), unchecked((int) 0xab2af7b4), unchecked((int) 0xefc34d20), unchecked((int) 0x2e096b7c), unchecked((int) 0x1741a254), unchecked((int) 0xe5b6a035), unchecked((int) 0x213d42f6), unchecked((int) 0x2c1c7c26), + unchecked((int) 0x61c2f50f), unchecked((int) 0x6552daf9), unchecked((int) 0xd2c231f8), unchecked((int) 0x25130f69), unchecked((int) 0xd8167fa2), unchecked((int) 0x0418f2c8), unchecked((int) 0x001a96a6), unchecked((int) 0x0d1526ab), + unchecked((int) 0x63315c21), unchecked((int) 0x5e0a72ec), unchecked((int) 0x49bafefd), unchecked((int) 0x187908d9), unchecked((int) 0x8d0dbd86), unchecked((int) 0x311170a7), unchecked((int) 0x3e9b640c), unchecked((int) 0xcc3e10d7), + unchecked((int) 0xd5cad3b6), unchecked((int) 0x0caec388), unchecked((int) 0xf73001e1), unchecked((int) 0x6c728aff), unchecked((int) 0x71eae2a1), unchecked((int) 0x1f9af36e), unchecked((int) 0xcfcbd12f), unchecked((int) 0xc1de8417), + unchecked((int) 0xac07be6b), unchecked((int) 0xcb44a1d8), unchecked((int) 0x8b9b0f56), unchecked((int) 0x013988c3), unchecked((int) 0xb1c52fca), unchecked((int) 0xb4be31cd), unchecked((int) 0xd8782806), unchecked((int) 0x12a3a4e2), + unchecked((int) 0x6f7de532), unchecked((int) 0x58fd7eb6), unchecked((int) 0xd01ee900), unchecked((int) 0x24adffc2), unchecked((int) 0xf4990fc5), unchecked((int) 0x9711aac5), unchecked((int) 0x001d7b95), unchecked((int) 0x82e5e7d2), + unchecked((int) 0x109873f6), unchecked((int) 0x00613096), unchecked((int) 0xc32d9521), unchecked((int) 0xada121ff), unchecked((int) 0x29908415), unchecked((int) 0x7fbb977f), unchecked((int) 0xaf9eb3db), unchecked((int) 0x29c9ed2a), + unchecked((int) 0x5ce2a465), unchecked((int) 0xa730f32c), unchecked((int) 0xd0aa3fe8), unchecked((int) 0x8a5cc091), unchecked((int) 0xd49e2ce7), unchecked((int) 0x0ce454a9), unchecked((int) 0xd60acd86), unchecked((int) 0x015f1919), + unchecked((int) 0x77079103), unchecked((int) 0xdea03af6), unchecked((int) 0x78a8565e), unchecked((int) 0xdee356df), unchecked((int) 0x21f05cbe), unchecked((int) 0x8b75e387), unchecked((int) 0xb3c50651), unchecked((int) 0xb8a5c3ef), + unchecked((int) 0xd8eeb6d2), unchecked((int) 0xe523be77), unchecked((int) 0xc2154529), unchecked((int) 0x2f69efdf), unchecked((int) 0xafe67afb), unchecked((int) 0xf470c4b2), unchecked((int) 0xf3e0eb5b), unchecked((int) 0xd6cc9876), + unchecked((int) 0x39e4460c), unchecked((int) 0x1fda8538), unchecked((int) 0x1987832f), unchecked((int) 0xca007367), unchecked((int) 0xa99144f8), unchecked((int) 0x296b299e), unchecked((int) 0x492fc295), unchecked((int) 0x9266beab), + unchecked((int) 0xb5676e69), unchecked((int) 0x9bd3ddda), unchecked((int) 0xdf7e052f), unchecked((int) 0xdb25701c), unchecked((int) 0x1b5e51ee), unchecked((int) 0xf65324e6), unchecked((int) 0x6afce36c), unchecked((int) 0x0316cc04), + unchecked((int) 0x8644213e), unchecked((int) 0xb7dc59d0), unchecked((int) 0x7965291f), unchecked((int) 0xccd6fd43), unchecked((int) 0x41823979), unchecked((int) 0x932bcdf6), unchecked((int) 0xb657c34d), unchecked((int) 0x4edfd282), + unchecked((int) 0x7ae5290c), unchecked((int) 0x3cb9536b), unchecked((int) 0x851e20fe), unchecked((int) 0x9833557e), unchecked((int) 0x13ecf0b0), unchecked((int) 0xd3ffb372), unchecked((int) 0x3f85c5c1), unchecked((int) 0x0aef7ed2) + }, + S5 = + { + unchecked((int) 0x7ec90c04), unchecked((int) 0x2c6e74b9), unchecked((int) 0x9b0e66df), unchecked((int) 0xa6337911), unchecked((int) 0xb86a7fff), unchecked((int) 0x1dd358f5), unchecked((int) 0x44dd9d44), unchecked((int) 0x1731167f), + unchecked((int) 0x08fbf1fa), unchecked((int) 0xe7f511cc), unchecked((int) 0xd2051b00), unchecked((int) 0x735aba00), unchecked((int) 0x2ab722d8), unchecked((int) 0x386381cb), unchecked((int) 0xacf6243a), unchecked((int) 0x69befd7a), + unchecked((int) 0xe6a2e77f), unchecked((int) 0xf0c720cd), unchecked((int) 0xc4494816), unchecked((int) 0xccf5c180), unchecked((int) 0x38851640), unchecked((int) 0x15b0a848), unchecked((int) 0xe68b18cb), unchecked((int) 0x4caadeff), + unchecked((int) 0x5f480a01), unchecked((int) 0x0412b2aa), unchecked((int) 0x259814fc), unchecked((int) 0x41d0efe2), unchecked((int) 0x4e40b48d), unchecked((int) 0x248eb6fb), unchecked((int) 0x8dba1cfe), unchecked((int) 0x41a99b02), + unchecked((int) 0x1a550a04), unchecked((int) 0xba8f65cb), unchecked((int) 0x7251f4e7), unchecked((int) 0x95a51725), unchecked((int) 0xc106ecd7), unchecked((int) 0x97a5980a), unchecked((int) 0xc539b9aa), unchecked((int) 0x4d79fe6a), + unchecked((int) 0xf2f3f763), unchecked((int) 0x68af8040), unchecked((int) 0xed0c9e56), unchecked((int) 0x11b4958b), unchecked((int) 0xe1eb5a88), unchecked((int) 0x8709e6b0), unchecked((int) 0xd7e07156), unchecked((int) 0x4e29fea7), + unchecked((int) 0x6366e52d), unchecked((int) 0x02d1c000), unchecked((int) 0xc4ac8e05), unchecked((int) 0x9377f571), unchecked((int) 0x0c05372a), unchecked((int) 0x578535f2), unchecked((int) 0x2261be02), unchecked((int) 0xd642a0c9), + unchecked((int) 0xdf13a280), unchecked((int) 0x74b55bd2), unchecked((int) 0x682199c0), unchecked((int) 0xd421e5ec), unchecked((int) 0x53fb3ce8), unchecked((int) 0xc8adedb3), unchecked((int) 0x28a87fc9), unchecked((int) 0x3d959981), + unchecked((int) 0x5c1ff900), unchecked((int) 0xfe38d399), unchecked((int) 0x0c4eff0b), unchecked((int) 0x062407ea), unchecked((int) 0xaa2f4fb1), unchecked((int) 0x4fb96976), unchecked((int) 0x90c79505), unchecked((int) 0xb0a8a774), + unchecked((int) 0xef55a1ff), unchecked((int) 0xe59ca2c2), unchecked((int) 0xa6b62d27), unchecked((int) 0xe66a4263), unchecked((int) 0xdf65001f), unchecked((int) 0x0ec50966), unchecked((int) 0xdfdd55bc), unchecked((int) 0x29de0655), + unchecked((int) 0x911e739a), unchecked((int) 0x17af8975), unchecked((int) 0x32c7911c), unchecked((int) 0x89f89468), unchecked((int) 0x0d01e980), unchecked((int) 0x524755f4), unchecked((int) 0x03b63cc9), unchecked((int) 0x0cc844b2), + unchecked((int) 0xbcf3f0aa), unchecked((int) 0x87ac36e9), unchecked((int) 0xe53a7426), unchecked((int) 0x01b3d82b), unchecked((int) 0x1a9e7449), unchecked((int) 0x64ee2d7e), unchecked((int) 0xcddbb1da), unchecked((int) 0x01c94910), + unchecked((int) 0xb868bf80), unchecked((int) 0x0d26f3fd), unchecked((int) 0x9342ede7), unchecked((int) 0x04a5c284), unchecked((int) 0x636737b6), unchecked((int) 0x50f5b616), unchecked((int) 0xf24766e3), unchecked((int) 0x8eca36c1), + unchecked((int) 0x136e05db), unchecked((int) 0xfef18391), unchecked((int) 0xfb887a37), unchecked((int) 0xd6e7f7d4), unchecked((int) 0xc7fb7dc9), unchecked((int) 0x3063fcdf), unchecked((int) 0xb6f589de), unchecked((int) 0xec2941da), + unchecked((int) 0x26e46695), unchecked((int) 0xb7566419), unchecked((int) 0xf654efc5), unchecked((int) 0xd08d58b7), unchecked((int) 0x48925401), unchecked((int) 0xc1bacb7f), unchecked((int) 0xe5ff550f), unchecked((int) 0xb6083049), + unchecked((int) 0x5bb5d0e8), unchecked((int) 0x87d72e5a), unchecked((int) 0xab6a6ee1), unchecked((int) 0x223a66ce), unchecked((int) 0xc62bf3cd), unchecked((int) 0x9e0885f9), unchecked((int) 0x68cb3e47), unchecked((int) 0x086c010f), + unchecked((int) 0xa21de820), unchecked((int) 0xd18b69de), unchecked((int) 0xf3f65777), unchecked((int) 0xfa02c3f6), unchecked((int) 0x407edac3), unchecked((int) 0xcbb3d550), unchecked((int) 0x1793084d), unchecked((int) 0xb0d70eba), + unchecked((int) 0x0ab378d5), unchecked((int) 0xd951fb0c), unchecked((int) 0xded7da56), unchecked((int) 0x4124bbe4), unchecked((int) 0x94ca0b56), unchecked((int) 0x0f5755d1), unchecked((int) 0xe0e1e56e), unchecked((int) 0x6184b5be), + unchecked((int) 0x580a249f), unchecked((int) 0x94f74bc0), unchecked((int) 0xe327888e), unchecked((int) 0x9f7b5561), unchecked((int) 0xc3dc0280), unchecked((int) 0x05687715), unchecked((int) 0x646c6bd7), unchecked((int) 0x44904db3), + unchecked((int) 0x66b4f0a3), unchecked((int) 0xc0f1648a), unchecked((int) 0x697ed5af), unchecked((int) 0x49e92ff6), unchecked((int) 0x309e374f), unchecked((int) 0x2cb6356a), unchecked((int) 0x85808573), unchecked((int) 0x4991f840), + unchecked((int) 0x76f0ae02), unchecked((int) 0x083be84d), unchecked((int) 0x28421c9a), unchecked((int) 0x44489406), unchecked((int) 0x736e4cb8), unchecked((int) 0xc1092910), unchecked((int) 0x8bc95fc6), unchecked((int) 0x7d869cf4), + unchecked((int) 0x134f616f), unchecked((int) 0x2e77118d), unchecked((int) 0xb31b2be1), unchecked((int) 0xaa90b472), unchecked((int) 0x3ca5d717), unchecked((int) 0x7d161bba), unchecked((int) 0x9cad9010), unchecked((int) 0xaf462ba2), + unchecked((int) 0x9fe459d2), unchecked((int) 0x45d34559), unchecked((int) 0xd9f2da13), unchecked((int) 0xdbc65487), unchecked((int) 0xf3e4f94e), unchecked((int) 0x176d486f), unchecked((int) 0x097c13ea), unchecked((int) 0x631da5c7), + unchecked((int) 0x445f7382), unchecked((int) 0x175683f4), unchecked((int) 0xcdc66a97), unchecked((int) 0x70be0288), unchecked((int) 0xb3cdcf72), unchecked((int) 0x6e5dd2f3), unchecked((int) 0x20936079), unchecked((int) 0x459b80a5), + unchecked((int) 0xbe60e2db), unchecked((int) 0xa9c23101), unchecked((int) 0xeba5315c), unchecked((int) 0x224e42f2), unchecked((int) 0x1c5c1572), unchecked((int) 0xf6721b2c), unchecked((int) 0x1ad2fff3), unchecked((int) 0x8c25404e), + unchecked((int) 0x324ed72f), unchecked((int) 0x4067b7fd), unchecked((int) 0x0523138e), unchecked((int) 0x5ca3bc78), unchecked((int) 0xdc0fd66e), unchecked((int) 0x75922283), unchecked((int) 0x784d6b17), unchecked((int) 0x58ebb16e), + unchecked((int) 0x44094f85), unchecked((int) 0x3f481d87), unchecked((int) 0xfcfeae7b), unchecked((int) 0x77b5ff76), unchecked((int) 0x8c2302bf), unchecked((int) 0xaaf47556), unchecked((int) 0x5f46b02a), unchecked((int) 0x2b092801), + unchecked((int) 0x3d38f5f7), unchecked((int) 0x0ca81f36), unchecked((int) 0x52af4a8a), unchecked((int) 0x66d5e7c0), unchecked((int) 0xdf3b0874), unchecked((int) 0x95055110), unchecked((int) 0x1b5ad7a8), unchecked((int) 0xf61ed5ad), + unchecked((int) 0x6cf6e479), unchecked((int) 0x20758184), unchecked((int) 0xd0cefa65), unchecked((int) 0x88f7be58), unchecked((int) 0x4a046826), unchecked((int) 0x0ff6f8f3), unchecked((int) 0xa09c7f70), unchecked((int) 0x5346aba0), + unchecked((int) 0x5ce96c28), unchecked((int) 0xe176eda3), unchecked((int) 0x6bac307f), unchecked((int) 0x376829d2), unchecked((int) 0x85360fa9), unchecked((int) 0x17e3fe2a), unchecked((int) 0x24b79767), unchecked((int) 0xf5a96b20), + unchecked((int) 0xd6cd2595), unchecked((int) 0x68ff1ebf), unchecked((int) 0x7555442c), unchecked((int) 0xf19f06be), unchecked((int) 0xf9e0659a), unchecked((int) 0xeeb9491d), unchecked((int) 0x34010718), unchecked((int) 0xbb30cab8), + unchecked((int) 0xe822fe15), unchecked((int) 0x88570983), unchecked((int) 0x750e6249), unchecked((int) 0xda627e55), unchecked((int) 0x5e76ffa8), unchecked((int) 0xb1534546), unchecked((int) 0x6d47de08), unchecked((int) 0xefe9e7d4) + }, + S6 = + { + unchecked((int) 0xf6fa8f9d), unchecked((int) 0x2cac6ce1), unchecked((int) 0x4ca34867), unchecked((int) 0xe2337f7c), unchecked((int) 0x95db08e7), unchecked((int) 0x016843b4), unchecked((int) 0xeced5cbc), unchecked((int) 0x325553ac), + unchecked((int) 0xbf9f0960), unchecked((int) 0xdfa1e2ed), unchecked((int) 0x83f0579d), unchecked((int) 0x63ed86b9), unchecked((int) 0x1ab6a6b8), unchecked((int) 0xde5ebe39), unchecked((int) 0xf38ff732), unchecked((int) 0x8989b138), + unchecked((int) 0x33f14961), unchecked((int) 0xc01937bd), unchecked((int) 0xf506c6da), unchecked((int) 0xe4625e7e), unchecked((int) 0xa308ea99), unchecked((int) 0x4e23e33c), unchecked((int) 0x79cbd7cc), unchecked((int) 0x48a14367), + unchecked((int) 0xa3149619), unchecked((int) 0xfec94bd5), unchecked((int) 0xa114174a), unchecked((int) 0xeaa01866), unchecked((int) 0xa084db2d), unchecked((int) 0x09a8486f), unchecked((int) 0xa888614a), unchecked((int) 0x2900af98), + unchecked((int) 0x01665991), unchecked((int) 0xe1992863), unchecked((int) 0xc8f30c60), unchecked((int) 0x2e78ef3c), unchecked((int) 0xd0d51932), unchecked((int) 0xcf0fec14), unchecked((int) 0xf7ca07d2), unchecked((int) 0xd0a82072), + unchecked((int) 0xfd41197e), unchecked((int) 0x9305a6b0), unchecked((int) 0xe86be3da), unchecked((int) 0x74bed3cd), unchecked((int) 0x372da53c), unchecked((int) 0x4c7f4448), unchecked((int) 0xdab5d440), unchecked((int) 0x6dba0ec3), + unchecked((int) 0x083919a7), unchecked((int) 0x9fbaeed9), unchecked((int) 0x49dbcfb0), unchecked((int) 0x4e670c53), unchecked((int) 0x5c3d9c01), unchecked((int) 0x64bdb941), unchecked((int) 0x2c0e636a), unchecked((int) 0xba7dd9cd), + unchecked((int) 0xea6f7388), unchecked((int) 0xe70bc762), unchecked((int) 0x35f29adb), unchecked((int) 0x5c4cdd8d), unchecked((int) 0xf0d48d8c), unchecked((int) 0xb88153e2), unchecked((int) 0x08a19866), unchecked((int) 0x1ae2eac8), + unchecked((int) 0x284caf89), unchecked((int) 0xaa928223), unchecked((int) 0x9334be53), unchecked((int) 0x3b3a21bf), unchecked((int) 0x16434be3), unchecked((int) 0x9aea3906), unchecked((int) 0xefe8c36e), unchecked((int) 0xf890cdd9), + unchecked((int) 0x80226dae), unchecked((int) 0xc340a4a3), unchecked((int) 0xdf7e9c09), unchecked((int) 0xa694a807), unchecked((int) 0x5b7c5ecc), unchecked((int) 0x221db3a6), unchecked((int) 0x9a69a02f), unchecked((int) 0x68818a54), + unchecked((int) 0xceb2296f), unchecked((int) 0x53c0843a), unchecked((int) 0xfe893655), unchecked((int) 0x25bfe68a), unchecked((int) 0xb4628abc), unchecked((int) 0xcf222ebf), unchecked((int) 0x25ac6f48), unchecked((int) 0xa9a99387), + unchecked((int) 0x53bddb65), unchecked((int) 0xe76ffbe7), unchecked((int) 0xe967fd78), unchecked((int) 0x0ba93563), unchecked((int) 0x8e342bc1), unchecked((int) 0xe8a11be9), unchecked((int) 0x4980740d), unchecked((int) 0xc8087dfc), + unchecked((int) 0x8de4bf99), unchecked((int) 0xa11101a0), unchecked((int) 0x7fd37975), unchecked((int) 0xda5a26c0), unchecked((int) 0xe81f994f), unchecked((int) 0x9528cd89), unchecked((int) 0xfd339fed), unchecked((int) 0xb87834bf), + unchecked((int) 0x5f04456d), unchecked((int) 0x22258698), unchecked((int) 0xc9c4c83b), unchecked((int) 0x2dc156be), unchecked((int) 0x4f628daa), unchecked((int) 0x57f55ec5), unchecked((int) 0xe2220abe), unchecked((int) 0xd2916ebf), + unchecked((int) 0x4ec75b95), unchecked((int) 0x24f2c3c0), unchecked((int) 0x42d15d99), unchecked((int) 0xcd0d7fa0), unchecked((int) 0x7b6e27ff), unchecked((int) 0xa8dc8af0), unchecked((int) 0x7345c106), unchecked((int) 0xf41e232f), + unchecked((int) 0x35162386), unchecked((int) 0xe6ea8926), unchecked((int) 0x3333b094), unchecked((int) 0x157ec6f2), unchecked((int) 0x372b74af), unchecked((int) 0x692573e4), unchecked((int) 0xe9a9d848), unchecked((int) 0xf3160289), + unchecked((int) 0x3a62ef1d), unchecked((int) 0xa787e238), unchecked((int) 0xf3a5f676), unchecked((int) 0x74364853), unchecked((int) 0x20951063), unchecked((int) 0x4576698d), unchecked((int) 0xb6fad407), unchecked((int) 0x592af950), + unchecked((int) 0x36f73523), unchecked((int) 0x4cfb6e87), unchecked((int) 0x7da4cec0), unchecked((int) 0x6c152daa), unchecked((int) 0xcb0396a8), unchecked((int) 0xc50dfe5d), unchecked((int) 0xfcd707ab), unchecked((int) 0x0921c42f), + unchecked((int) 0x89dff0bb), unchecked((int) 0x5fe2be78), unchecked((int) 0x448f4f33), unchecked((int) 0x754613c9), unchecked((int) 0x2b05d08d), unchecked((int) 0x48b9d585), unchecked((int) 0xdc049441), unchecked((int) 0xc8098f9b), + unchecked((int) 0x7dede786), unchecked((int) 0xc39a3373), unchecked((int) 0x42410005), unchecked((int) 0x6a091751), unchecked((int) 0x0ef3c8a6), unchecked((int) 0x890072d6), unchecked((int) 0x28207682), unchecked((int) 0xa9a9f7be), + unchecked((int) 0xbf32679d), unchecked((int) 0xd45b5b75), unchecked((int) 0xb353fd00), unchecked((int) 0xcbb0e358), unchecked((int) 0x830f220a), unchecked((int) 0x1f8fb214), unchecked((int) 0xd372cf08), unchecked((int) 0xcc3c4a13), + unchecked((int) 0x8cf63166), unchecked((int) 0x061c87be), unchecked((int) 0x88c98f88), unchecked((int) 0x6062e397), unchecked((int) 0x47cf8e7a), unchecked((int) 0xb6c85283), unchecked((int) 0x3cc2acfb), unchecked((int) 0x3fc06976), + unchecked((int) 0x4e8f0252), unchecked((int) 0x64d8314d), unchecked((int) 0xda3870e3), unchecked((int) 0x1e665459), unchecked((int) 0xc10908f0), unchecked((int) 0x513021a5), unchecked((int) 0x6c5b68b7), unchecked((int) 0x822f8aa0), + unchecked((int) 0x3007cd3e), unchecked((int) 0x74719eef), unchecked((int) 0xdc872681), unchecked((int) 0x073340d4), unchecked((int) 0x7e432fd9), unchecked((int) 0x0c5ec241), unchecked((int) 0x8809286c), unchecked((int) 0xf592d891), + unchecked((int) 0x08a930f6), unchecked((int) 0x957ef305), unchecked((int) 0xb7fbffbd), unchecked((int) 0xc266e96f), unchecked((int) 0x6fe4ac98), unchecked((int) 0xb173ecc0), unchecked((int) 0xbc60b42a), unchecked((int) 0x953498da), + unchecked((int) 0xfba1ae12), unchecked((int) 0x2d4bd736), unchecked((int) 0x0f25faab), unchecked((int) 0xa4f3fceb), unchecked((int) 0xe2969123), unchecked((int) 0x257f0c3d), unchecked((int) 0x9348af49), unchecked((int) 0x361400bc), + unchecked((int) 0xe8816f4a), unchecked((int) 0x3814f200), unchecked((int) 0xa3f94043), unchecked((int) 0x9c7a54c2), unchecked((int) 0xbc704f57), unchecked((int) 0xda41e7f9), unchecked((int) 0xc25ad33a), unchecked((int) 0x54f4a084), + unchecked((int) 0xb17f5505), unchecked((int) 0x59357cbe), unchecked((int) 0xedbd15c8), unchecked((int) 0x7f97c5ab), unchecked((int) 0xba5ac7b5), unchecked((int) 0xb6f6deaf), unchecked((int) 0x3a479c3a), unchecked((int) 0x5302da25), + unchecked((int) 0x653d7e6a), unchecked((int) 0x54268d49), unchecked((int) 0x51a477ea), unchecked((int) 0x5017d55b), unchecked((int) 0xd7d25d88), unchecked((int) 0x44136c76), unchecked((int) 0x0404a8c8), unchecked((int) 0xb8e5a121), + unchecked((int) 0xb81a928a), unchecked((int) 0x60ed5869), unchecked((int) 0x97c55b96), unchecked((int) 0xeaec991b), unchecked((int) 0x29935913), unchecked((int) 0x01fdb7f1), unchecked((int) 0x088e8dfa), unchecked((int) 0x9ab6f6f5), + unchecked((int) 0x3b4cbf9f), unchecked((int) 0x4a5de3ab), unchecked((int) 0xe6051d35), unchecked((int) 0xa0e1d855), unchecked((int) 0xd36b4cf1), unchecked((int) 0xf544edeb), unchecked((int) 0xb0e93524), unchecked((int) 0xbebb8fbd), + unchecked((int) 0xa2d762cf), unchecked((int) 0x49c92f54), unchecked((int) 0x38b5f331), unchecked((int) 0x7128a454), unchecked((int) 0x48392905), unchecked((int) 0xa65b1db8), unchecked((int) 0x851c97bd), unchecked((int) 0xd675cf2f) + }, + S7 = + { + unchecked((int) 0x85e04019), unchecked((int) 0x332bf567), unchecked((int) 0x662dbfff), unchecked((int) 0xcfc65693), unchecked((int) 0x2a8d7f6f), unchecked((int) 0xab9bc912), unchecked((int) 0xde6008a1), unchecked((int) 0x2028da1f), + unchecked((int) 0x0227bce7), unchecked((int) 0x4d642916), unchecked((int) 0x18fac300), unchecked((int) 0x50f18b82), unchecked((int) 0x2cb2cb11), unchecked((int) 0xb232e75c), unchecked((int) 0x4b3695f2), unchecked((int) 0xb28707de), + unchecked((int) 0xa05fbcf6), unchecked((int) 0xcd4181e9), unchecked((int) 0xe150210c), unchecked((int) 0xe24ef1bd), unchecked((int) 0xb168c381), unchecked((int) 0xfde4e789), unchecked((int) 0x5c79b0d8), unchecked((int) 0x1e8bfd43), + unchecked((int) 0x4d495001), unchecked((int) 0x38be4341), unchecked((int) 0x913cee1d), unchecked((int) 0x92a79c3f), unchecked((int) 0x089766be), unchecked((int) 0xbaeeadf4), unchecked((int) 0x1286becf), unchecked((int) 0xb6eacb19), + unchecked((int) 0x2660c200), unchecked((int) 0x7565bde4), unchecked((int) 0x64241f7a), unchecked((int) 0x8248dca9), unchecked((int) 0xc3b3ad66), unchecked((int) 0x28136086), unchecked((int) 0x0bd8dfa8), unchecked((int) 0x356d1cf2), + unchecked((int) 0x107789be), unchecked((int) 0xb3b2e9ce), unchecked((int) 0x0502aa8f), unchecked((int) 0x0bc0351e), unchecked((int) 0x166bf52a), unchecked((int) 0xeb12ff82), unchecked((int) 0xe3486911), unchecked((int) 0xd34d7516), + unchecked((int) 0x4e7b3aff), unchecked((int) 0x5f43671b), unchecked((int) 0x9cf6e037), unchecked((int) 0x4981ac83), unchecked((int) 0x334266ce), unchecked((int) 0x8c9341b7), unchecked((int) 0xd0d854c0), unchecked((int) 0xcb3a6c88), + unchecked((int) 0x47bc2829), unchecked((int) 0x4725ba37), unchecked((int) 0xa66ad22b), unchecked((int) 0x7ad61f1e), unchecked((int) 0x0c5cbafa), unchecked((int) 0x4437f107), unchecked((int) 0xb6e79962), unchecked((int) 0x42d2d816), + unchecked((int) 0x0a961288), unchecked((int) 0xe1a5c06e), unchecked((int) 0x13749e67), unchecked((int) 0x72fc081a), unchecked((int) 0xb1d139f7), unchecked((int) 0xf9583745), unchecked((int) 0xcf19df58), unchecked((int) 0xbec3f756), + unchecked((int) 0xc06eba30), unchecked((int) 0x07211b24), unchecked((int) 0x45c28829), unchecked((int) 0xc95e317f), unchecked((int) 0xbc8ec511), unchecked((int) 0x38bc46e9), unchecked((int) 0xc6e6fa14), unchecked((int) 0xbae8584a), + unchecked((int) 0xad4ebc46), unchecked((int) 0x468f508b), unchecked((int) 0x7829435f), unchecked((int) 0xf124183b), unchecked((int) 0x821dba9f), unchecked((int) 0xaff60ff4), unchecked((int) 0xea2c4e6d), unchecked((int) 0x16e39264), + unchecked((int) 0x92544a8b), unchecked((int) 0x009b4fc3), unchecked((int) 0xaba68ced), unchecked((int) 0x9ac96f78), unchecked((int) 0x06a5b79a), unchecked((int) 0xb2856e6e), unchecked((int) 0x1aec3ca9), unchecked((int) 0xbe838688), + unchecked((int) 0x0e0804e9), unchecked((int) 0x55f1be56), unchecked((int) 0xe7e5363b), unchecked((int) 0xb3a1f25d), unchecked((int) 0xf7debb85), unchecked((int) 0x61fe033c), unchecked((int) 0x16746233), unchecked((int) 0x3c034c28), + unchecked((int) 0xda6d0c74), unchecked((int) 0x79aac56c), unchecked((int) 0x3ce4e1ad), unchecked((int) 0x51f0c802), unchecked((int) 0x98f8f35a), unchecked((int) 0x1626a49f), unchecked((int) 0xeed82b29), unchecked((int) 0x1d382fe3), + unchecked((int) 0x0c4fb99a), unchecked((int) 0xbb325778), unchecked((int) 0x3ec6d97b), unchecked((int) 0x6e77a6a9), unchecked((int) 0xcb658b5c), unchecked((int) 0xd45230c7), unchecked((int) 0x2bd1408b), unchecked((int) 0x60c03eb7), + unchecked((int) 0xb9068d78), unchecked((int) 0xa33754f4), unchecked((int) 0xf430c87d), unchecked((int) 0xc8a71302), unchecked((int) 0xb96d8c32), unchecked((int) 0xebd4e7be), unchecked((int) 0xbe8b9d2d), unchecked((int) 0x7979fb06), + unchecked((int) 0xe7225308), unchecked((int) 0x8b75cf77), unchecked((int) 0x11ef8da4), unchecked((int) 0xe083c858), unchecked((int) 0x8d6b786f), unchecked((int) 0x5a6317a6), unchecked((int) 0xfa5cf7a0), unchecked((int) 0x5dda0033), + unchecked((int) 0xf28ebfb0), unchecked((int) 0xf5b9c310), unchecked((int) 0xa0eac280), unchecked((int) 0x08b9767a), unchecked((int) 0xa3d9d2b0), unchecked((int) 0x79d34217), unchecked((int) 0x021a718d), unchecked((int) 0x9ac6336a), + unchecked((int) 0x2711fd60), unchecked((int) 0x438050e3), unchecked((int) 0x069908a8), unchecked((int) 0x3d7fedc4), unchecked((int) 0x826d2bef), unchecked((int) 0x4eeb8476), unchecked((int) 0x488dcf25), unchecked((int) 0x36c9d566), + unchecked((int) 0x28e74e41), unchecked((int) 0xc2610aca), unchecked((int) 0x3d49a9cf), unchecked((int) 0xbae3b9df), unchecked((int) 0xb65f8de6), unchecked((int) 0x92aeaf64), unchecked((int) 0x3ac7d5e6), unchecked((int) 0x9ea80509), + unchecked((int) 0xf22b017d), unchecked((int) 0xa4173f70), unchecked((int) 0xdd1e16c3), unchecked((int) 0x15e0d7f9), unchecked((int) 0x50b1b887), unchecked((int) 0x2b9f4fd5), unchecked((int) 0x625aba82), unchecked((int) 0x6a017962), + unchecked((int) 0x2ec01b9c), unchecked((int) 0x15488aa9), unchecked((int) 0xd716e740), unchecked((int) 0x40055a2c), unchecked((int) 0x93d29a22), unchecked((int) 0xe32dbf9a), unchecked((int) 0x058745b9), unchecked((int) 0x3453dc1e), + unchecked((int) 0xd699296e), unchecked((int) 0x496cff6f), unchecked((int) 0x1c9f4986), unchecked((int) 0xdfe2ed07), unchecked((int) 0xb87242d1), unchecked((int) 0x19de7eae), unchecked((int) 0x053e561a), unchecked((int) 0x15ad6f8c), + unchecked((int) 0x66626c1c), unchecked((int) 0x7154c24c), unchecked((int) 0xea082b2a), unchecked((int) 0x93eb2939), unchecked((int) 0x17dcb0f0), unchecked((int) 0x58d4f2ae), unchecked((int) 0x9ea294fb), unchecked((int) 0x52cf564c), + unchecked((int) 0x9883fe66), unchecked((int) 0x2ec40581), unchecked((int) 0x763953c3), unchecked((int) 0x01d6692e), unchecked((int) 0xd3a0c108), unchecked((int) 0xa1e7160e), unchecked((int) 0xe4f2dfa6), unchecked((int) 0x693ed285), + unchecked((int) 0x74904698), unchecked((int) 0x4c2b0edd), unchecked((int) 0x4f757656), unchecked((int) 0x5d393378), unchecked((int) 0xa132234f), unchecked((int) 0x3d321c5d), unchecked((int) 0xc3f5e194), unchecked((int) 0x4b269301), + unchecked((int) 0xc79f022f), unchecked((int) 0x3c997e7e), unchecked((int) 0x5e4f9504), unchecked((int) 0x3ffafbbd), unchecked((int) 0x76f7ad0e), unchecked((int) 0x296693f4), unchecked((int) 0x3d1fce6f), unchecked((int) 0xc61e45be), + unchecked((int) 0xd3b5ab34), unchecked((int) 0xf72bf9b7), unchecked((int) 0x1b0434c0), unchecked((int) 0x4e72b567), unchecked((int) 0x5592a33d), unchecked((int) 0xb5229301), unchecked((int) 0xcfd2a87f), unchecked((int) 0x60aeb767), + unchecked((int) 0x1814386b), unchecked((int) 0x30bcc33d), unchecked((int) 0x38a0c07d), unchecked((int) 0xfd1606f2), unchecked((int) 0xc363519b), unchecked((int) 0x589dd390), unchecked((int) 0x5479f8e6), unchecked((int) 0x1cb8d647), + unchecked((int) 0x97fd61a9), unchecked((int) 0xea7759f4), unchecked((int) 0x2d57539d), unchecked((int) 0x569a58cf), unchecked((int) 0xe84e63ad), unchecked((int) 0x462e1b78), unchecked((int) 0x6580f87e), unchecked((int) 0xf3817914), + unchecked((int) 0x91da55f4), unchecked((int) 0x40a230f3), unchecked((int) 0xd1988f35), unchecked((int) 0xb6e318d2), unchecked((int) 0x3ffa50bc), unchecked((int) 0x3d40f021), unchecked((int) 0xc3c0bdae), unchecked((int) 0x4958c24c), + unchecked((int) 0x518f36b2), unchecked((int) 0x84b1d370), unchecked((int) 0x0fedce83), unchecked((int) 0x878ddada), unchecked((int) 0xf2a279c7), unchecked((int) 0x94e01be8), unchecked((int) 0x90716f4b), unchecked((int) 0x954b8aa3) + }, + S8 = + { + unchecked((int) 0xe216300d), unchecked((int) 0xbbddfffc), unchecked((int) 0xa7ebdabd), unchecked((int) 0x35648095), unchecked((int) 0x7789f8b7), unchecked((int) 0xe6c1121b), unchecked((int) 0x0e241600), unchecked((int) 0x052ce8b5), + unchecked((int) 0x11a9cfb0), unchecked((int) 0xe5952f11), unchecked((int) 0xece7990a), unchecked((int) 0x9386d174), unchecked((int) 0x2a42931c), unchecked((int) 0x76e38111), unchecked((int) 0xb12def3a), unchecked((int) 0x37ddddfc), + unchecked((int) 0xde9adeb1), unchecked((int) 0x0a0cc32c), unchecked((int) 0xbe197029), unchecked((int) 0x84a00940), unchecked((int) 0xbb243a0f), unchecked((int) 0xb4d137cf), unchecked((int) 0xb44e79f0), unchecked((int) 0x049eedfd), + unchecked((int) 0x0b15a15d), unchecked((int) 0x480d3168), unchecked((int) 0x8bbbde5a), unchecked((int) 0x669ded42), unchecked((int) 0xc7ece831), unchecked((int) 0x3f8f95e7), unchecked((int) 0x72df191b), unchecked((int) 0x7580330d), + unchecked((int) 0x94074251), unchecked((int) 0x5c7dcdfa), unchecked((int) 0xabbe6d63), unchecked((int) 0xaa402164), unchecked((int) 0xb301d40a), unchecked((int) 0x02e7d1ca), unchecked((int) 0x53571dae), unchecked((int) 0x7a3182a2), + unchecked((int) 0x12a8ddec), unchecked((int) 0xfdaa335d), unchecked((int) 0x176f43e8), unchecked((int) 0x71fb46d4), unchecked((int) 0x38129022), unchecked((int) 0xce949ad4), unchecked((int) 0xb84769ad), unchecked((int) 0x965bd862), + unchecked((int) 0x82f3d055), unchecked((int) 0x66fb9767), unchecked((int) 0x15b80b4e), unchecked((int) 0x1d5b47a0), unchecked((int) 0x4cfde06f), unchecked((int) 0xc28ec4b8), unchecked((int) 0x57e8726e), unchecked((int) 0x647a78fc), + unchecked((int) 0x99865d44), unchecked((int) 0x608bd593), unchecked((int) 0x6c200e03), unchecked((int) 0x39dc5ff6), unchecked((int) 0x5d0b00a3), unchecked((int) 0xae63aff2), unchecked((int) 0x7e8bd632), unchecked((int) 0x70108c0c), + unchecked((int) 0xbbd35049), unchecked((int) 0x2998df04), unchecked((int) 0x980cf42a), unchecked((int) 0x9b6df491), unchecked((int) 0x9e7edd53), unchecked((int) 0x06918548), unchecked((int) 0x58cb7e07), unchecked((int) 0x3b74ef2e), + unchecked((int) 0x522fffb1), unchecked((int) 0xd24708cc), unchecked((int) 0x1c7e27cd), unchecked((int) 0xa4eb215b), unchecked((int) 0x3cf1d2e2), unchecked((int) 0x19b47a38), unchecked((int) 0x424f7618), unchecked((int) 0x35856039), + unchecked((int) 0x9d17dee7), unchecked((int) 0x27eb35e6), unchecked((int) 0xc9aff67b), unchecked((int) 0x36baf5b8), unchecked((int) 0x09c467cd), unchecked((int) 0xc18910b1), unchecked((int) 0xe11dbf7b), unchecked((int) 0x06cd1af8), + unchecked((int) 0x7170c608), unchecked((int) 0x2d5e3354), unchecked((int) 0xd4de495a), unchecked((int) 0x64c6d006), unchecked((int) 0xbcc0c62c), unchecked((int) 0x3dd00db3), unchecked((int) 0x708f8f34), unchecked((int) 0x77d51b42), + unchecked((int) 0x264f620f), unchecked((int) 0x24b8d2bf), unchecked((int) 0x15c1b79e), unchecked((int) 0x46a52564), unchecked((int) 0xf8d7e54e), unchecked((int) 0x3e378160), unchecked((int) 0x7895cda5), unchecked((int) 0x859c15a5), + unchecked((int) 0xe6459788), unchecked((int) 0xc37bc75f), unchecked((int) 0xdb07ba0c), unchecked((int) 0x0676a3ab), unchecked((int) 0x7f229b1e), unchecked((int) 0x31842e7b), unchecked((int) 0x24259fd7), unchecked((int) 0xf8bef472), + unchecked((int) 0x835ffcb8), unchecked((int) 0x6df4c1f2), unchecked((int) 0x96f5b195), unchecked((int) 0xfd0af0fc), unchecked((int) 0xb0fe134c), unchecked((int) 0xe2506d3d), unchecked((int) 0x4f9b12ea), unchecked((int) 0xf215f225), + unchecked((int) 0xa223736f), unchecked((int) 0x9fb4c428), unchecked((int) 0x25d04979), unchecked((int) 0x34c713f8), unchecked((int) 0xc4618187), unchecked((int) 0xea7a6e98), unchecked((int) 0x7cd16efc), unchecked((int) 0x1436876c), + unchecked((int) 0xf1544107), unchecked((int) 0xbedeee14), unchecked((int) 0x56e9af27), unchecked((int) 0xa04aa441), unchecked((int) 0x3cf7c899), unchecked((int) 0x92ecbae6), unchecked((int) 0xdd67016d), unchecked((int) 0x151682eb), + unchecked((int) 0xa842eedf), unchecked((int) 0xfdba60b4), unchecked((int) 0xf1907b75), unchecked((int) 0x20e3030f), unchecked((int) 0x24d8c29e), unchecked((int) 0xe139673b), unchecked((int) 0xefa63fb8), unchecked((int) 0x71873054), + unchecked((int) 0xb6f2cf3b), unchecked((int) 0x9f326442), unchecked((int) 0xcb15a4cc), unchecked((int) 0xb01a4504), unchecked((int) 0xf1e47d8d), unchecked((int) 0x844a1be5), unchecked((int) 0xbae7dfdc), unchecked((int) 0x42cbda70), + unchecked((int) 0xcd7dae0a), unchecked((int) 0x57e85b7a), unchecked((int) 0xd53f5af6), unchecked((int) 0x20cf4d8c), unchecked((int) 0xcea4d428), unchecked((int) 0x79d130a4), unchecked((int) 0x3486ebfb), unchecked((int) 0x33d3cddc), + unchecked((int) 0x77853b53), unchecked((int) 0x37effcb5), unchecked((int) 0xc5068778), unchecked((int) 0xe580b3e6), unchecked((int) 0x4e68b8f4), unchecked((int) 0xc5c8b37e), unchecked((int) 0x0d809ea2), unchecked((int) 0x398feb7c), + unchecked((int) 0x132a4f94), unchecked((int) 0x43b7950e), unchecked((int) 0x2fee7d1c), unchecked((int) 0x223613bd), unchecked((int) 0xdd06caa2), unchecked((int) 0x37df932b), unchecked((int) 0xc4248289), unchecked((int) 0xacf3ebc3), + unchecked((int) 0x5715f6b7), unchecked((int) 0xef3478dd), unchecked((int) 0xf267616f), unchecked((int) 0xc148cbe4), unchecked((int) 0x9052815e), unchecked((int) 0x5e410fab), unchecked((int) 0xb48a2465), unchecked((int) 0x2eda7fa4), + unchecked((int) 0xe87b40e4), unchecked((int) 0xe98ea084), unchecked((int) 0x5889e9e1), unchecked((int) 0xefd390fc), unchecked((int) 0xdd07d35b), unchecked((int) 0xdb485694), unchecked((int) 0x38d7e5b2), unchecked((int) 0x57720101), + unchecked((int) 0x730edebc), unchecked((int) 0x5b643113), unchecked((int) 0x94917e4f), unchecked((int) 0x503c2fba), unchecked((int) 0x646f1282), unchecked((int) 0x7523d24a), unchecked((int) 0xe0779695), unchecked((int) 0xf9c17a8f), + unchecked((int) 0x7a5b2121), unchecked((int) 0xd187b896), unchecked((int) 0x29263a4d), unchecked((int) 0xba510cdf), unchecked((int) 0x81f47c9f), unchecked((int) 0xad1163ed), unchecked((int) 0xea7b5965), unchecked((int) 0x1a00726e), + unchecked((int) 0x11403092), unchecked((int) 0x00da6d77), unchecked((int) 0x4a0cdd61), unchecked((int) 0xad1f4603), unchecked((int) 0x605bdfb0), unchecked((int) 0x9eedc364), unchecked((int) 0x22ebe6a8), unchecked((int) 0xcee7d28a), + unchecked((int) 0xa0e736a0), unchecked((int) 0x5564a6b9), unchecked((int) 0x10853209), unchecked((int) 0xc7eb8f37), unchecked((int) 0x2de705ca), unchecked((int) 0x8951570f), unchecked((int) 0xdf09822b), unchecked((int) 0xbd691a6c), + unchecked((int) 0xaa12e4f2), unchecked((int) 0x87451c0f), unchecked((int) 0xe0f6a27a), unchecked((int) 0x3ada4819), unchecked((int) 0x4cf1764f), unchecked((int) 0x0d771c2b), unchecked((int) 0x67cdb156), unchecked((int) 0x350d8384), + unchecked((int) 0x5938fa0f), unchecked((int) 0x42399ef3), unchecked((int) 0x36997b07), unchecked((int) 0x0e84093d), unchecked((int) 0x4aa93e61), unchecked((int) 0x8360d87b), unchecked((int) 0x1fa98b0c), unchecked((int) 0x1149382c), + unchecked((int) 0xe97625a5), unchecked((int) 0x0614d1b7), unchecked((int) 0x0e25244b), unchecked((int) 0x0c768347), unchecked((int) 0x589e8d82), unchecked((int) 0x0d2059d1), unchecked((int) 0xa466bb1e), unchecked((int) 0xf8da0a82), + unchecked((int) 0x04f19130), unchecked((int) 0xba6e4ec0), unchecked((int) 0x99265164), unchecked((int) 0x1ee7230d), unchecked((int) 0x50b2ad80), unchecked((int) 0xeaee6801), unchecked((int) 0x8db2a283), unchecked((int) 0xea8bf59e) + }; + + //==================================== + // Useful constants + //==================================== + + internal static readonly int MAX_ROUNDS = 16; + internal static readonly int RED_ROUNDS = 12; + + private const int BLOCK_SIZE = 8; // bytes = 64 bits + + private int [] _Kr = new int[17]; // the rotating round key + private int [] _Km = new int[17]; // the masking round key + + private bool _encrypting; + + private byte[] _workingKey; + private int _rounds = MAX_ROUNDS; + + public Cast5Engine() + { + } + + /** + * initialise a CAST cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("Invalid parameter passed to "+ AlgorithmName +" init - " + parameters.GetType().ToString()); + + _encrypting = forEncryption; + _workingKey = ((KeyParameter)parameters).GetKey(); + SetKey(_workingKey); + } + + public virtual string AlgorithmName + { + get { return "CAST5"; } + } + + public virtual bool IsPartialBlockOkay + { + get { return false; } + } + + public virtual int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + int blockSize = GetBlockSize(); + if (_workingKey == null) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + blockSize) > input.Length) + throw new DataLengthException("Input buffer too short"); + if ((outOff + blockSize) > output.Length) + throw new DataLengthException("Output buffer too short"); + + if (_encrypting) + { + return EncryptBlock(input, inOff, output, outOff); + } + else + { + return DecryptBlock(input, inOff, output, outOff); + } + } + + public virtual void Reset() + { + } + + public virtual int GetBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + /* + * Creates the subkeys using the same nomenclature + * as described in RFC2144. + * + * See section 2.4 + */ + internal virtual void SetKey(byte[] key) + { + /* + * Determine the key size here, if required + * + * if keysize <= 80bits, use 12 rounds instead of 16 + * if keysize < 128bits, pad with 0 + * + * Typical key sizes => 40, 64, 80, 128 + */ + + if (key.Length < 11) + { + _rounds = RED_ROUNDS; + } + + int [] z = new int[16]; + int [] x = new int[16]; + + int z03, z47, z8B, zCF; + int x03, x47, x8B, xCF; + + /* copy the key into x */ + for (int i=0; i< key.Length; i++) + { + x[i] = (int)(key[i] & 0xff); + } + + /* + * This will look different because the selection of + * bytes from the input key I've already chosen the + * correct int. + */ + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + + z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]]; + + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + _Km[ 1]= S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]]; + _Km[ 2]= S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]]; + _Km[ 3]= S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]]; + _Km[ 4]= S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]]; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + _Km[ 5]= S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]]; + _Km[ 6]= S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]]; + _Km[ 7]= S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]]; + _Km[ 8]= S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]]; + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + _Km[ 9]= S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]]; + _Km[10]= S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]]; + _Km[11]= S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]]; + _Km[12]= S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]]; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + _Km[13]= S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]]; + _Km[14]= S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]]; + _Km[15]= S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]]; + _Km[16]= S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]]; + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + _Kr[ 1]=(S5[z[0x8]]^S6[z[0x9]]^S7[z[0x7]]^S8[z[0x6]] ^ S5[z[0x2]])&0x1f; + _Kr[ 2]=(S5[z[0xA]]^S6[z[0xB]]^S7[z[0x5]]^S8[z[0x4]] ^ S6[z[0x6]])&0x1f; + _Kr[ 3]=(S5[z[0xC]]^S6[z[0xD]]^S7[z[0x3]]^S8[z[0x2]] ^ S7[z[0x9]])&0x1f; + _Kr[ 4]=(S5[z[0xE]]^S6[z[0xF]]^S7[z[0x1]]^S8[z[0x0]] ^ S8[z[0xC]])&0x1f; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + _Kr[ 5]=(S5[x[0x3]]^S6[x[0x2]]^S7[x[0xC]]^S8[x[0xD]]^S5[x[0x8]])&0x1f; + _Kr[ 6]=(S5[x[0x1]]^S6[x[0x0]]^S7[x[0xE]]^S8[x[0xF]]^S6[x[0xD]])&0x1f; + _Kr[ 7]=(S5[x[0x7]]^S6[x[0x6]]^S7[x[0x8]]^S8[x[0x9]]^S7[x[0x3]])&0x1f; + _Kr[ 8]=(S5[x[0x5]]^S6[x[0x4]]^S7[x[0xA]]^S8[x[0xB]]^S8[x[0x7]])&0x1f; + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^S5[x[0xD]] ^S6[x[0xF]] ^S7[x[0xC]] ^S8[x[0xE]] ^S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^S5[z[0x0]] ^S6[z[0x2]] ^S7[z[0x1]] ^S8[z[0x3]] ^S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^S5[z[0x7]] ^S6[z[0x6]] ^S7[z[0x5]] ^S8[z[0x4]] ^S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^S5[z[0xA]] ^S6[z[0x9]] ^S7[z[0xB]] ^S8[z[0x8]] ^S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + _Kr[ 9]=(S5[z[0x3]]^S6[z[0x2]]^S7[z[0xC]]^S8[z[0xD]]^S5[z[0x9]])&0x1f; + _Kr[10]=(S5[z[0x1]]^S6[z[0x0]]^S7[z[0xE]]^S8[z[0xF]]^S6[z[0xc]])&0x1f; + _Kr[11]=(S5[z[0x7]]^S6[z[0x6]]^S7[z[0x8]]^S8[z[0x9]]^S7[z[0x2]])&0x1f; + _Kr[12]=(S5[z[0x5]]^S6[z[0x4]]^S7[z[0xA]]^S8[z[0xB]]^S8[z[0x6]])&0x1f; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^S5[z[0x5]] ^S6[z[0x7]] ^S7[z[0x4]] ^S8[z[0x6]] ^S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^S5[x[0x0]] ^S6[x[0x2]] ^S7[x[0x1]] ^S8[x[0x3]] ^S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^S5[x[0x7]] ^S6[x[0x6]] ^S7[x[0x5]] ^S8[x[0x4]] ^S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^S5[x[0xA]] ^S6[x[0x9]] ^S7[x[0xB]] ^S8[x[0x8]] ^S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + _Kr[13]=(S5[x[0x8]]^S6[x[0x9]]^S7[x[0x7]]^S8[x[0x6]]^S5[x[0x3]])&0x1f; + _Kr[14]=(S5[x[0xA]]^S6[x[0xB]]^S7[x[0x5]]^S8[x[0x4]]^S6[x[0x7]])&0x1f; + _Kr[15]=(S5[x[0xC]]^S6[x[0xD]]^S7[x[0x3]]^S8[x[0x2]]^S7[x[0x8]])&0x1f; + _Kr[16]=(S5[x[0xE]]^S6[x[0xF]]^S7[x[0x1]]^S8[x[0x0]]^S8[x[0xD]])&0x1f; + } + + /** + * Encrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param src The plaintext buffer + * @param srcIndex An offset into src + * @param dst The ciphertext buffer + * @param dstIndex An offset into dst + */ + internal virtual int EncryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int [] result = new int[2]; + + // process the input block + // batch the units up into a 32 bit chunk and go for it + // the array is in bytes, the increment is 8x8 bits = 64 + + int L0 = BytesTo32bits(src, srcIndex); + int R0 = BytesTo32bits(src, srcIndex + 4); + + CAST_Encipher(L0, R0, result); + + // now stuff them into the destination block + Bits32ToBytes(result[0], dst, dstIndex); + Bits32ToBytes(result[1], dst, dstIndex + 4); + + return BLOCK_SIZE; + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param src The plaintext buffer + * @param srcIndex An offset into src + * @param dst The ciphertext buffer + * @param dstIndex An offset into dst + */ + internal virtual int DecryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int [] result = new int[2]; + + // process the input block + // batch the units up into a 32 bit chunk and go for it + // the array is in bytes, the increment is 8x8 bits = 64 + int L16 = BytesTo32bits(src, srcIndex); + int R16 = BytesTo32bits(src, srcIndex+4); + + CAST_Decipher(L16, R16, result); + + // now stuff them into the destination block + Bits32ToBytes(result[0], dst, dstIndex); + Bits32ToBytes(result[1], dst, dstIndex+4); + + return BLOCK_SIZE; + } + + /** + * The first of the three processing functions for the + * encryption and decryption. + * + * @param D the input to be processed + * @param Kmi the mask to be used from Km[n] + * @param Kri the rotation value to be used + * + */ + internal static int F1(int D, int Kmi, int Kri) + { + int I = Kmi + D; + I = I << Kri | (int) ((uint) I >> (32-Kri)); + return ((S1[((uint) I >>24)&0xff]^S2[((uint)I>>16)&0xff])-S3[((uint)I>> 8)&0xff])+ + S4[(I )&0xff]; + } + + /** + * The second of the three processing functions for the + * encryption and decryption. + * + * @param D the input to be processed + * @param Kmi the mask to be used from Km[n] + * @param Kri the rotation value to be used + * + */ + internal static int F2(int D, int Kmi, int Kri) + { + int I = Kmi ^ D; + I = I << Kri | (int) ((uint)I >> (32-Kri)); + return ((S1[((uint)I>>24)&0xff]-S2[((uint)I>>16)&0xff])+S3[((uint)I>> 8)&0xff])^ + S4[(I )&0xff]; + } + + /** + * The third of the three processing functions for the + * encryption and decryption. + * + * @param D the input to be processed + * @param Kmi the mask to be used from Km[n] + * @param Kri the rotation value to be used + * + */ + internal static int F3(int D, int Kmi, int Kri) + { + int I = Kmi - D; + I = I << Kri | (int) ((uint)I >> (32-Kri)); + return ((S1[((uint)I>>24)&0xff]+S2[((uint)I>>16)&0xff])^S3[((uint)I>> 8)&0xff])- + S4[(I )&0xff]; + } + + /** + * Does the 16 rounds to encrypt the block. + * + * @param L0 the LH-32bits of the plaintext block + * @param R0 the RH-32bits of the plaintext block + */ + internal void CAST_Encipher(int L0, int R0, int [] result) + { + int Lp = L0; // the previous value, equiv to L[i-1] + int Rp = R0; // equivalent to R[i-1] + + /* + * numbering consistent with paper to make + * checking and validating easier + */ + int Li = L0, Ri = R0; + + for (int i = 1; i<=_rounds ; i++) + { + Lp = Li; + Rp = Ri; + + Li = Rp; + switch (i) + { + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]); + break; + case 2: + case 5: + case 8: + case 11: + case 14: + Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]); + break; + case 3: + case 6: + case 9: + case 12: + case 15: + Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]); + break; + } + } + + result[0] = Ri; + result[1] = Li; + + return; + } + + internal void CAST_Decipher(int L16, int R16, int [] result) + { + int Lp = L16; // the previous value, equiv to L[i-1] + int Rp = R16; // equivalent to R[i-1] + + /* + * numbering consistent with paper to make + * checking and validating easier + */ + int Li = L16, Ri = R16; + + for (int i = _rounds; i > 0; i--) + { + Lp = Li; + Rp = Ri; + + Li = Rp; + switch (i) + { + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]); + break; + case 2: + case 5: + case 8: + case 11: + case 14: + Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]); + break; + case 3: + case 6: + case 9: + case 12: + case 15: + Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]); + break; + } + } + + result[0] = Ri; + result[1] = Li; + + return; + } + + internal static void Bits32ToInts(int inData, int[] b, int offset) + { + b[offset + 3] = (inData & 0xff); + b[offset + 2] = (int) (((uint) inData >> 8) & 0xff); + b[offset + 1] = (int) (((uint)inData >> 16) & 0xff); + b[offset] = (int) (((uint)inData >> 24) & 0xff); + } + + internal static int IntsTo32bits(int[] b, int i) + { + int rv = 0; + + rv = ((b[i] & 0xff) << 24) | + ((b[i+1] & 0xff) << 16) | + ((b[i+2] & 0xff) << 8) | + ((b[i+3] & 0xff)); + + return rv; + } + + internal static void Bits32ToBytes(int inData, byte[] b, int offset) + { + b[offset + 3] = (byte)inData; + b[offset + 2] = (byte)((uint)inData >> 8); + b[offset + 1] = (byte)((uint)inData >> 16); + b[offset] = (byte)((uint)inData >> 24); + } + + internal static 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)); + } + } + +} diff --git a/src/core/srcbc/crypto/engines/Cast6Engine.cs b/src/core/srcbc/crypto/engines/Cast6Engine.cs new file mode 100644 index 0000000..0ed331a --- /dev/null +++ b/src/core/srcbc/crypto/engines/Cast6Engine.cs @@ -0,0 +1,277 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * A class that provides CAST6 key encryption operations, + * such as encoding data and generating keys. + * + * All the algorithms herein are from the Internet RFC + * + * RFC2612 - CAST6 (128bit block, 128-256bit key) + * + * and implement a simplified cryptography interface. + */ + public sealed class Cast6Engine + : Cast5Engine + { + //==================================== + // Useful constants + //==================================== + private const int ROUNDS = 12; + private const int BLOCK_SIZE = 16; // bytes = 128 bits + + /* + * Put the round and mask keys into an array. + * Kr0[i] => _Kr[i*4 + 0] + */ + private int []_Kr = new int[ROUNDS*4]; // the rotating round key(s) + private int []_Km = new int[ROUNDS*4]; // the masking round key(s) + + /* + * Key setup + */ + private int []_Tr = new int[24 * 8]; + private int []_Tm = new int[24 * 8]; + private int[] _workingKey = new int[8]; + + public Cast6Engine() + { + } + + public override string AlgorithmName + { + get { return "CAST6"; } + } + + public override void Reset() + { + } + + public override int GetBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + /* + * Creates the subkeys using the same nomenclature + * as described in RFC2612. + * + * See section 2.4 + */ + internal override void SetKey( + byte[] key) + { + int Cm = 0x5a827999; + int Mm = 0x6ed9eba1; + int Cr = 19; + int Mr = 17; + /* + * Determine the key size here, if required + * + * if keysize < 256 bytes, pad with 0 + * + * Typical key sizes => 128, 160, 192, 224, 256 + */ + for (int i=0; i< 24; i++) + { + for (int j=0; j< 8; j++) + { + _Tm[i*8 + j] = Cm; + Cm += Mm; //mod 2^32; + _Tr[i*8 + j] = Cr; + Cr = (Cr + Mr) & 0x1f; // mod 32 + } + } + + byte[] tmpKey = new byte[64]; + key.CopyTo(tmpKey, 0); + + // now create ABCDEFGH + for (int i = 0; i < 8; i++) + { + _workingKey[i] = BytesTo32bits(tmpKey, i*4); + } + + // Generate the key schedule + for (int i = 0; i < 12; i++) + { + // KAPPA <- W2i(KAPPA) + int i2 = i*2 *8; + _workingKey[6] ^= F1(_workingKey[7], _Tm[i2], _Tr[i2]); + _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]); + _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]); + _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]); + _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]); + _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]); + _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]); + _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]); + // KAPPA <- W2i+1(KAPPA) + i2 = (i*2 + 1)*8; + _workingKey[6] ^= F1(_workingKey[7], _Tm[i2], _Tr[i2]); + _workingKey[5] ^= F2(_workingKey[6], _Tm[i2+1], _Tr[i2+1]); + _workingKey[4] ^= F3(_workingKey[5], _Tm[i2+2], _Tr[i2+2]); + _workingKey[3] ^= F1(_workingKey[4], _Tm[i2+3], _Tr[i2+3]); + _workingKey[2] ^= F2(_workingKey[3], _Tm[i2+4], _Tr[i2+4]); + _workingKey[1] ^= F3(_workingKey[2], _Tm[i2+5], _Tr[i2+5]); + _workingKey[0] ^= F1(_workingKey[1], _Tm[i2+6], _Tr[i2+6]); + _workingKey[7] ^= F2(_workingKey[0], _Tm[i2+7], _Tr[i2+7]); + // Kr_(i) <- KAPPA + _Kr[i*4] = _workingKey[0] & 0x1f; + _Kr[i*4 + 1] = _workingKey[2] & 0x1f; + _Kr[i*4 + 2] = _workingKey[4] & 0x1f; + _Kr[i*4 + 3] = _workingKey[6] & 0x1f; + // Km_(i) <- KAPPA + _Km[i*4] = _workingKey[7]; + _Km[i*4 + 1] = _workingKey[5]; + _Km[i*4 + 2] = _workingKey[3]; + _Km[i*4 + 3] = _workingKey[1]; + } + } + + /** + * Encrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param src The plaintext buffer + * @param srcIndex An offset into src + * @param dst The ciphertext buffer + * @param dstIndex An offset into dst + */ + internal override int EncryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int[] result = new int[4]; + // process the input block + // batch the units up into 4x32 bit chunks and go for it + int A = BytesTo32bits(src, srcIndex); + int B = BytesTo32bits(src, srcIndex + 4); + int C = BytesTo32bits(src, srcIndex + 8); + int D = BytesTo32bits(src, srcIndex + 12); + CAST_Encipher(A, B, C, D, result); + // now stuff them into the destination block + Bits32ToBytes(result[0], dst, dstIndex); + Bits32ToBytes(result[1], dst, dstIndex + 4); + Bits32ToBytes(result[2], dst, dstIndex + 8); + Bits32ToBytes(result[3], dst, dstIndex + 12); + return BLOCK_SIZE; + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param src The plaintext buffer + * @param srcIndex An offset into src + * @param dst The ciphertext buffer + * @param dstIndex An offset into dst + */ + internal override int DecryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int[] result = new int[4]; + // process the input block + // batch the units up into 4x32 bit chunks and go for it + int A = BytesTo32bits(src, srcIndex); + int B = BytesTo32bits(src, srcIndex + 4); + int C = BytesTo32bits(src, srcIndex + 8); + int D = BytesTo32bits(src, srcIndex + 12); + CAST_Decipher(A, B, C, D, result); + // now stuff them into the destination block + Bits32ToBytes(result[0], dst, dstIndex); + Bits32ToBytes(result[1], dst, dstIndex + 4); + Bits32ToBytes(result[2], dst, dstIndex + 8); + Bits32ToBytes(result[3], dst, dstIndex + 12); + return BLOCK_SIZE; + } + + /** + * Does the 12 quad rounds rounds to encrypt the block. + * + * @param A the 00-31 bits of the plaintext block + * @param B the 32-63 bits of the plaintext block + * @param C the 64-95 bits of the plaintext block + * @param D the 96-127 bits of the plaintext block + * @param result the resulting ciphertext + */ + private void CAST_Encipher( + int A, + int B, + int C, + int D, + int[] result) + { + for (int i = 0; i < 6; i++) + { + int x = i*4; + // BETA <- Qi(BETA) + C ^= F1(D, _Km[x], _Kr[x]); + B ^= F2(C, _Km[x + 1], _Kr[x + 1]); + A ^= F3(B, _Km[x + 2], _Kr[x + 2]); + D ^= F1(A, _Km[x + 3], _Kr[x + 3]); + } + for (int i = 6; i < 12; i++) + { + int x = i*4; + // BETA <- QBARi(BETA) + D ^= F1(A, _Km[x + 3], _Kr[x + 3]); + A ^= F3(B, _Km[x + 2], _Kr[x + 2]); + B ^= F2(C, _Km[x + 1], _Kr[x + 1]); + C ^= F1(D, _Km[x], _Kr[x]); + } + result[0] = A; + result[1] = B; + result[2] = C; + result[3] = D; + } + + /** + * Does the 12 quad rounds rounds to decrypt the block. + * + * @param A the 00-31 bits of the ciphertext block + * @param B the 32-63 bits of the ciphertext block + * @param C the 64-95 bits of the ciphertext block + * @param D the 96-127 bits of the ciphertext block + * @param result the resulting plaintext + */ + private void CAST_Decipher( + int A, + int B, + int C, + int D, + int[] result) + { + for (int i = 0; i < 6; i++) + { + int x = (11-i)*4; + // BETA <- Qi(BETA) + C ^= F1(D, _Km[x], _Kr[x]); + B ^= F2(C, _Km[x + 1], _Kr[x + 1]); + A ^= F3(B, _Km[x + 2], _Kr[x + 2]); + D ^= F1(A, _Km[x + 3], _Kr[x + 3]); + } + for (int i=6; i<12; i++) + { + int x = (11-i)*4; + // BETA <- QBARi(BETA) + D ^= F1(A, _Km[x + 3], _Kr[x + 3]); + A ^= F3(B, _Km[x + 2], _Kr[x + 2]); + B ^= F2(C, _Km[x + 1], _Kr[x + 1]); + C ^= F1(D, _Km[x], _Kr[x]); + } + result[0] = A; + result[1] = B; + result[2] = C; + result[3] = D; + } + } +} diff --git a/src/core/srcbc/crypto/engines/DesEdeEngine.cs b/src/core/srcbc/crypto/engines/DesEdeEngine.cs new file mode 100644 index 0000000..455a3a7 --- /dev/null +++ b/src/core/srcbc/crypto/engines/DesEdeEngine.cs @@ -0,0 +1,96 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// A class that provides a basic DESede (or Triple DES) engine. + public class DesEdeEngine + : DesEngine + { + private int[] workingKey1, workingKey2, workingKey3; + private bool forEncryption; + + /** + * initialise a DESede cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + { + throw new ArgumentException("invalid parameter passed to DESede init - " + parameters.GetType().ToString()); + } + + byte[] keyMaster = ((KeyParameter)parameters).GetKey(); + byte[] key1 = new byte[8], key2 = new byte[8], key3 = new byte[8]; + this.forEncryption = forEncryption; + if (keyMaster.Length == 24) + { + Array.Copy(keyMaster, 0, key1, 0, key1.Length); + Array.Copy(keyMaster, 8, key2, 0, key2.Length); + Array.Copy(keyMaster, 16, key3, 0, key3.Length); + workingKey1 = GenerateWorkingKey(forEncryption, key1); + workingKey2 = GenerateWorkingKey(!forEncryption, key2); + workingKey3 = GenerateWorkingKey(forEncryption, key3); + } + else // 16 byte key + { + Array.Copy(keyMaster, 0, key1, 0, key1.Length); + Array.Copy(keyMaster, 8, key2, 0, key2.Length); + workingKey1 = GenerateWorkingKey(forEncryption, key1); + workingKey2 = GenerateWorkingKey(!forEncryption, key2); + workingKey3 = workingKey1; + } + } + + public override string AlgorithmName + { + get { return "DESede"; } + } + + public override int GetBlockSize() + { + return BLOCK_SIZE; + } + + public override int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey1 == null) + throw new InvalidOperationException("DESede engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (forEncryption) + { + DesFunc(workingKey1, input, inOff, output, outOff); + DesFunc(workingKey2, output, outOff, output, outOff); + DesFunc(workingKey3, output, outOff, output, outOff); + } + else + { + DesFunc(workingKey3, input, inOff, output, outOff); + DesFunc(workingKey2, output, outOff, output, outOff); + DesFunc(workingKey1, output, outOff, output, outOff); + } + + return BLOCK_SIZE; + } + + public override void Reset() + { + } + } +} diff --git a/src/core/srcbc/crypto/engines/DesEdeWrapEngine.cs b/src/core/srcbc/crypto/engines/DesEdeWrapEngine.cs new file mode 100644 index 0000000..348a523 --- /dev/null +++ b/src/core/srcbc/crypto/engines/DesEdeWrapEngine.cs @@ -0,0 +1,299 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Wrap keys according to + * + * draft-ietf-smime-key-wrap-01.txt. + *

+ * Note: + *

    + *
  • this is based on a draft, and as such is subject to change - don't use this class for anything requiring long term storage.
  • + *
  • if you are using this to wrap triple-des keys you need to set the + * parity bits on the key and, if it's a two-key triple-des key, pad it + * yourself.
  • + *
+ *

+ */ + public class DesEdeWrapEngine + : IWrapper + { + /** Field engine */ + private CbcBlockCipher engine; + /** Field param */ + private KeyParameter param; + /** Field paramPlusIV */ + private ParametersWithIV paramPlusIV; + /** Field iv */ + private byte[] iv; + /** Field forWrapping */ + private bool forWrapping; + /** Field IV2 */ + private static readonly byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, + (byte) 0x2c, (byte) 0x79, (byte) 0xe8, + (byte) 0x21, (byte) 0x05 }; + + // + // checksum digest + // + private readonly IDigest sha1 = new Sha1Digest(); + private readonly byte[] digest = new byte[20]; + + /** + * Method init + * + * @param forWrapping + * @param param + */ + public void Init( + bool forWrapping, + ICipherParameters parameters) + { + this.forWrapping = forWrapping; + this.engine = new CbcBlockCipher(new DesEdeEngine()); + + SecureRandom sr; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom pr = (ParametersWithRandom) parameters; + parameters = pr.Parameters; + sr = pr.Random; + } + else + { + sr = new SecureRandom(); + } + + if (parameters is KeyParameter) + { + this.param = (KeyParameter) parameters; + if (this.forWrapping) + { + // Hm, we have no IV but we want to wrap ?!? + // well, then we have to create our own IV. + this.iv = new byte[8]; + sr.NextBytes(iv); + + this.paramPlusIV = new ParametersWithIV(this.param, this.iv); + } + } + else if (parameters is ParametersWithIV) + { + if (!forWrapping) + throw new ArgumentException("You should not supply an IV for unwrapping"); + + this.paramPlusIV = (ParametersWithIV) parameters; + this.iv = this.paramPlusIV.GetIV(); + this.param = (KeyParameter) this.paramPlusIV.Parameters; + + if (this.iv.Length != 8) + throw new ArgumentException("IV is not 8 octets", "parameters"); + } + } + + /** + * Method GetAlgorithmName + * + * @return + */ + public string AlgorithmName + { + get { return "DESede"; } + } + + /** + * Method wrap + * + * @param in + * @param inOff + * @param inLen + * @return + */ + public byte[] Wrap( + byte[] input, + int inOff, + int length) + { + if (!forWrapping) + { + throw new InvalidOperationException("Not initialized for wrapping"); + } + + byte[] keyToBeWrapped = new byte[length]; + Array.Copy(input, inOff, keyToBeWrapped, 0, length); + // Compute the CMS Key Checksum, (section 5.6.1), call this CKS. + byte[] CKS = CalculateCmsKeyChecksum(keyToBeWrapped); + // Let WKCKS = WK || CKS where || is concatenation. + byte[] WKCKS = new byte[keyToBeWrapped.Length + CKS.Length]; + Array.Copy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.Length); + Array.Copy(CKS, 0, WKCKS, keyToBeWrapped.Length, CKS.Length); + // Encrypt WKCKS in CBC mode using KEK as the key and IV as the + // initialization vector. Call the results TEMP1. + byte [] TEMP1 = new byte[WKCKS.Length]; + Array.Copy(WKCKS, 0, TEMP1, 0, WKCKS.Length); + int noOfBlocks = WKCKS.Length / engine.GetBlockSize(); + int extraBytes = WKCKS.Length % engine.GetBlockSize(); + if (extraBytes != 0) { + throw new InvalidOperationException("Not multiple of block length"); + } + engine.Init(true, paramPlusIV); + for (int i = 0; i < noOfBlocks; i++) { + int currentBytePos = i * engine.GetBlockSize(); + engine.ProcessBlock(TEMP1, currentBytePos, TEMP1, currentBytePos); + } + // Left TEMP2 = IV || TEMP1. + byte[] TEMP2 = new byte[this.iv.Length + TEMP1.Length]; + Array.Copy(this.iv, 0, TEMP2, 0, this.iv.Length); + Array.Copy(TEMP1, 0, TEMP2, this.iv.Length, TEMP1.Length); + // Reverse the order of the octets in TEMP2 and call the result TEMP3. + byte[] TEMP3 = new byte[TEMP2.Length]; + for (int i = 0; i < TEMP2.Length; i++) { + TEMP3[i] = TEMP2[TEMP2.Length - (i + 1)]; + } + // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector + // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired + // result. It is 40 octets long if a 168 bit key is being wrapped. + ParametersWithIV param2 = new ParametersWithIV(this.param, IV2); + this.engine.Init(true, param2); + for (int i = 0; i < noOfBlocks + 1; i++) { + int currentBytePos = i * engine.GetBlockSize(); + engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos); + } + return TEMP3; + } + + /** + * Method unwrap + * + * @param in + * @param inOff + * @param inLen + * @return + * @throws InvalidCipherTextException + */ + public byte[] Unwrap( + byte[] input, + int inOff, + int length) + { + if (forWrapping) + { + throw new InvalidOperationException("Not set for unwrapping"); + } + if (input == null) + { + throw new InvalidCipherTextException("Null pointer as ciphertext"); + } + if (length % engine.GetBlockSize() != 0) + { + throw new InvalidCipherTextException( + "Ciphertext not multiple of " + engine.GetBlockSize()); + } + + /* + // Check if the length of the cipher text is reasonable given the key + // type. It must be 40 bytes for a 168 bit key and either 32, 40, or + // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported + // or inconsistent with the algorithm for which the key is intended, + // return error. + // + // we do not accept 168 bit keys. it has to be 192 bit. + int lengthA = (estimatedKeyLengthInBit / 8) + 16; + int lengthB = estimatedKeyLengthInBit % 8; + if ((lengthA != keyToBeUnwrapped.Length) || (lengthB != 0)) { + throw new XMLSecurityException("empty"); + } + */ + // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK + // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3. + ParametersWithIV param2 = new ParametersWithIV(this.param, IV2); + this.engine.Init(false, param2); + byte [] TEMP3 = new byte[length]; + Array.Copy(input, inOff, TEMP3, 0, length); + for (int i = 0; i < (TEMP3.Length / engine.GetBlockSize()); i++) { + int currentBytePos = i * engine.GetBlockSize(); + engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos); + } + // Reverse the order of the octets in TEMP3 and call the result TEMP2. + byte[] TEMP2 = new byte[TEMP3.Length]; + for (int i = 0; i < TEMP3.Length; i++) { + TEMP2[i] = TEMP3[TEMP3.Length - (i + 1)]; + } + // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets. + this.iv = new byte[8]; + byte[] TEMP1 = new byte[TEMP2.Length - 8]; + Array.Copy(TEMP2, 0, this.iv, 0, 8); + Array.Copy(TEMP2, 8, TEMP1, 0, TEMP2.Length - 8); + // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV + // found in the previous step. Call the result WKCKS. + this.paramPlusIV = new ParametersWithIV(this.param, this.iv); + this.engine.Init(false, this.paramPlusIV); + byte[] WKCKS = new byte[TEMP1.Length]; + Array.Copy(TEMP1, 0, WKCKS, 0, TEMP1.Length); + for (int i = 0; i < (WKCKS.Length / engine.GetBlockSize()); i++) { + int currentBytePos = i * engine.GetBlockSize(); + engine.ProcessBlock(WKCKS, currentBytePos, WKCKS, currentBytePos); + } + // Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are + // those octets before the CKS. + byte[] result = new byte[WKCKS.Length - 8]; + byte[] CKStoBeVerified = new byte[8]; + Array.Copy(WKCKS, 0, result, 0, WKCKS.Length - 8); + Array.Copy(WKCKS, WKCKS.Length - 8, CKStoBeVerified, 0, 8); + // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare + // with the CKS extracted in the above step. If they are not equal, return error. + if (!CheckCmsKeyChecksum(result, CKStoBeVerified)) { + throw new InvalidCipherTextException( + "Checksum inside ciphertext is corrupted"); + } + // WK is the wrapped key, now extracted for use in data decryption. + return result; + } + + /** + * Some key wrap algorithms make use of the Key Checksum defined + * in CMS [CMS-Algorithms]. This is used to provide an integrity + * check value for the key being wrapped. The algorithm is + * + * - 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 + * @return + * @throws Exception + * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum + */ + private byte[] CalculateCmsKeyChecksum( + byte[] key) + { + byte[] result = new byte[8]; + + sha1.BlockUpdate(key, 0, key.Length); + sha1.DoFinal(digest, 0); + + Array.Copy(digest, 0, result, 0, 8); + + return result; + } + + /** + * @param key + * @param checksum + * @return + * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum + */ + private bool CheckCmsKeyChecksum( + byte[] key, + byte[] checksum) + { + return Arrays.AreEqual(CalculateCmsKeyChecksum(key), checksum); + } + } +} diff --git a/src/core/srcbc/crypto/engines/DesEngine.cs b/src/core/srcbc/crypto/engines/DesEngine.cs new file mode 100644 index 0000000..05368d2 --- /dev/null +++ b/src/core/srcbc/crypto/engines/DesEngine.cs @@ -0,0 +1,493 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// A class that provides a basic DES engine. + public class DesEngine + : IBlockCipher + { + internal const int BLOCK_SIZE = 8; + + private int[] workingKey; + + public virtual int[] GetWorkingKey() + { + return workingKey; + } + + /** + * initialise a DES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to DES init - " + parameters.GetType().ToString()); + + workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)parameters).GetKey()); + } + + public virtual string AlgorithmName + { + get { return "DES"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public virtual int GetBlockSize() + { + return BLOCK_SIZE; + } + + public virtual int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + throw new InvalidOperationException("DES engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + DesFunc(workingKey, input, inOff, output, outOff); + + return BLOCK_SIZE; + } + + public virtual void Reset() + { + } + + /** + * what follows is mainly taken from "Applied Cryptography", by + * Bruce Schneier, however it also bears great resemblance to Richard + * Outerbridge's D3DES... + */ + + private static readonly short[] Df_Key = + { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10, + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 + }; + + private static readonly short[] bytebit = + { + 128, 64, 32, 16, 8, 4, 2, 1 + }; + + private static readonly int[] bigbyte = + { + 0x800000, 0x400000, 0x200000, 0x100000, + 0x80000, 0x40000, 0x20000, 0x10000, + 0x8000, 0x4000, 0x2000, 0x1000, + 0x800, 0x400, 0x200, 0x100, + 0x80, 0x40, 0x20, 0x10, + 0x8, 0x4, 0x2, 0x1 + }; + + /* + * Use the key schedule specified in the Standard (ANSI X3.92-1981). + */ + private static readonly byte[] pc1 = + { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 + }; + + private static readonly byte[] totrot = + { + 1, 2, 4, 6, 8, 10, 12, 14, + 15, 17, 19, 21, 23, 25, 27, 28 + }; + + private static readonly byte[] pc2 = + { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 + }; + + private static readonly int[] SP1 = + { + unchecked((int) 0x01010400), unchecked((int) 0x00000000), unchecked((int) 0x00010000), unchecked((int) 0x01010404), + unchecked((int) 0x01010004), unchecked((int) 0x00010404), unchecked((int) 0x00000004), unchecked((int) 0x00010000), + unchecked((int) 0x00000400), unchecked((int) 0x01010400), unchecked((int) 0x01010404), unchecked((int) 0x00000400), + unchecked((int) 0x01000404), unchecked((int) 0x01010004), unchecked((int) 0x01000000), unchecked((int) 0x00000004), + unchecked((int) 0x00000404), unchecked((int) 0x01000400), unchecked((int) 0x01000400), unchecked((int) 0x00010400), + unchecked((int) 0x00010400), unchecked((int) 0x01010000), unchecked((int) 0x01010000), unchecked((int) 0x01000404), + unchecked((int) 0x00010004), unchecked((int) 0x01000004), unchecked((int) 0x01000004), unchecked((int) 0x00010004), + unchecked((int) 0x00000000), unchecked((int) 0x00000404), unchecked((int) 0x00010404), unchecked((int) 0x01000000), + unchecked((int) 0x00010000), unchecked((int) 0x01010404), unchecked((int) 0x00000004), unchecked((int) 0x01010000), + unchecked((int) 0x01010400), unchecked((int) 0x01000000), unchecked((int) 0x01000000), unchecked((int) 0x00000400), + unchecked((int) 0x01010004), unchecked((int) 0x00010000), unchecked((int) 0x00010400), unchecked((int) 0x01000004), + unchecked((int) 0x00000400), unchecked((int) 0x00000004), unchecked((int) 0x01000404), unchecked((int) 0x00010404), + unchecked((int) 0x01010404), unchecked((int) 0x00010004), unchecked((int) 0x01010000), unchecked((int) 0x01000404), + unchecked((int) 0x01000004), unchecked((int) 0x00000404), unchecked((int) 0x00010404), unchecked((int) 0x01010400), + unchecked((int) 0x00000404), unchecked((int) 0x01000400), unchecked((int) 0x01000400), unchecked((int) 0x00000000), + unchecked((int) 0x00010004), unchecked((int) 0x00010400), unchecked((int) 0x00000000), unchecked((int) 0x01010004) + }; + + private static readonly int[] SP2 = + { + unchecked((int) 0x80108020), unchecked((int) 0x80008000), unchecked((int) 0x00008000), unchecked((int) 0x00108020), + unchecked((int) 0x00100000), unchecked((int) 0x00000020), unchecked((int) 0x80100020), unchecked((int) 0x80008020), + unchecked((int) 0x80000020), unchecked((int) 0x80108020), unchecked((int) 0x80108000), unchecked((int) 0x80000000), + unchecked((int) 0x80008000), unchecked((int) 0x00100000), unchecked((int) 0x00000020), unchecked((int) 0x80100020), + unchecked((int) 0x00108000), unchecked((int) 0x00100020), unchecked((int) 0x80008020), unchecked((int) 0x00000000), + unchecked((int) 0x80000000), unchecked((int) 0x00008000), unchecked((int) 0x00108020), unchecked((int) 0x80100000), + unchecked((int) 0x00100020), unchecked((int) 0x80000020), unchecked((int) 0x00000000), unchecked((int) 0x00108000), + unchecked((int) 0x00008020), unchecked((int) 0x80108000), unchecked((int) 0x80100000), unchecked((int) 0x00008020), + unchecked((int) 0x00000000), unchecked((int) 0x00108020), unchecked((int) 0x80100020), unchecked((int) 0x00100000), + unchecked((int) 0x80008020), unchecked((int) 0x80100000), unchecked((int) 0x80108000), unchecked((int) 0x00008000), + unchecked((int) 0x80100000), unchecked((int) 0x80008000), unchecked((int) 0x00000020), unchecked((int) 0x80108020), + unchecked((int) 0x00108020), unchecked((int) 0x00000020), unchecked((int) 0x00008000), unchecked((int) 0x80000000), + unchecked((int) 0x00008020), unchecked((int) 0x80108000), unchecked((int) 0x00100000), unchecked((int) 0x80000020), + unchecked((int) 0x00100020), unchecked((int) 0x80008020), unchecked((int) 0x80000020), unchecked((int) 0x00100020), + unchecked((int) 0x00108000), unchecked((int) 0x00000000), unchecked((int) 0x80008000), unchecked((int) 0x00008020), + unchecked((int) 0x80000000), unchecked((int) 0x80100020), unchecked((int) 0x80108020), unchecked((int) 0x00108000) + }; + + private static readonly int[] SP3 = + { + unchecked((int) 0x00000208), unchecked((int) 0x08020200), unchecked((int) 0x00000000), unchecked((int) 0x08020008), + unchecked((int) 0x08000200), unchecked((int) 0x00000000), unchecked((int) 0x00020208), unchecked((int) 0x08000200), + unchecked((int) 0x00020008), unchecked((int) 0x08000008), unchecked((int) 0x08000008), unchecked((int) 0x00020000), + unchecked((int) 0x08020208), unchecked((int) 0x00020008), unchecked((int) 0x08020000), unchecked((int) 0x00000208), + unchecked((int) 0x08000000), unchecked((int) 0x00000008), unchecked((int) 0x08020200), unchecked((int) 0x00000200), + unchecked((int) 0x00020200), unchecked((int) 0x08020000), unchecked((int) 0x08020008), unchecked((int) 0x00020208), + unchecked((int) 0x08000208), unchecked((int) 0x00020200), unchecked((int) 0x00020000), unchecked((int) 0x08000208), + unchecked((int) 0x00000008), unchecked((int) 0x08020208), unchecked((int) 0x00000200), unchecked((int) 0x08000000), + unchecked((int) 0x08020200), unchecked((int) 0x08000000), unchecked((int) 0x00020008), unchecked((int) 0x00000208), + unchecked((int) 0x00020000), unchecked((int) 0x08020200), unchecked((int) 0x08000200), unchecked((int) 0x00000000), + unchecked((int) 0x00000200), unchecked((int) 0x00020008), unchecked((int) 0x08020208), unchecked((int) 0x08000200), + unchecked((int) 0x08000008), unchecked((int) 0x00000200), unchecked((int) 0x00000000), unchecked((int) 0x08020008), + unchecked((int) 0x08000208), unchecked((int) 0x00020000), unchecked((int) 0x08000000), unchecked((int) 0x08020208), + unchecked((int) 0x00000008), unchecked((int) 0x00020208), unchecked((int) 0x00020200), unchecked((int) 0x08000008), + unchecked((int) 0x08020000), unchecked((int) 0x08000208), unchecked((int) 0x00000208), unchecked((int) 0x08020000), + unchecked((int) 0x00020208), unchecked((int) 0x00000008), unchecked((int) 0x08020008), unchecked((int) 0x00020200) + }; + + private static readonly int[] SP4 = + { + unchecked((int) 0x00802001), unchecked((int) 0x00002081), unchecked((int) 0x00002081), unchecked((int) 0x00000080), + unchecked((int) 0x00802080), unchecked((int) 0x00800081), unchecked((int) 0x00800001), unchecked((int) 0x00002001), + unchecked((int) 0x00000000), unchecked((int) 0x00802000), unchecked((int) 0x00802000), unchecked((int) 0x00802081), + unchecked((int) 0x00000081), unchecked((int) 0x00000000), unchecked((int) 0x00800080), unchecked((int) 0x00800001), + unchecked((int) 0x00000001), unchecked((int) 0x00002000), unchecked((int) 0x00800000), unchecked((int) 0x00802001), + unchecked((int) 0x00000080), unchecked((int) 0x00800000), unchecked((int) 0x00002001), unchecked((int) 0x00002080), + unchecked((int) 0x00800081), unchecked((int) 0x00000001), unchecked((int) 0x00002080), unchecked((int) 0x00800080), + unchecked((int) 0x00002000), unchecked((int) 0x00802080), unchecked((int) 0x00802081), unchecked((int) 0x00000081), + unchecked((int) 0x00800080), unchecked((int) 0x00800001), unchecked((int) 0x00802000), unchecked((int) 0x00802081), + unchecked((int) 0x00000081), unchecked((int) 0x00000000), unchecked((int) 0x00000000), unchecked((int) 0x00802000), + unchecked((int) 0x00002080), unchecked((int) 0x00800080), unchecked((int) 0x00800081), unchecked((int) 0x00000001), + unchecked((int) 0x00802001), unchecked((int) 0x00002081), unchecked((int) 0x00002081), unchecked((int) 0x00000080), + unchecked((int) 0x00802081), unchecked((int) 0x00000081), unchecked((int) 0x00000001), unchecked((int) 0x00002000), + unchecked((int) 0x00800001), unchecked((int) 0x00002001), unchecked((int) 0x00802080), unchecked((int) 0x00800081), + unchecked((int) 0x00002001), unchecked((int) 0x00002080), unchecked((int) 0x00800000), unchecked((int) 0x00802001), + unchecked((int) 0x00000080), unchecked((int) 0x00800000), unchecked((int) 0x00002000), unchecked((int) 0x00802080) + }; + + private static readonly int[] SP5 = + { + unchecked((int) 0x00000100), unchecked((int) 0x02080100), unchecked((int) 0x02080000), unchecked((int) 0x42000100), + unchecked((int) 0x00080000), unchecked((int) 0x00000100), unchecked((int) 0x40000000), unchecked((int) 0x02080000), + unchecked((int) 0x40080100), unchecked((int) 0x00080000), unchecked((int) 0x02000100), unchecked((int) 0x40080100), + unchecked((int) 0x42000100), unchecked((int) 0x42080000), unchecked((int) 0x00080100), unchecked((int) 0x40000000), + unchecked((int) 0x02000000), unchecked((int) 0x40080000), unchecked((int) 0x40080000), unchecked((int) 0x00000000), + unchecked((int) 0x40000100), unchecked((int) 0x42080100), unchecked((int) 0x42080100), unchecked((int) 0x02000100), + unchecked((int) 0x42080000), unchecked((int) 0x40000100), unchecked((int) 0x00000000), unchecked((int) 0x42000000), + unchecked((int) 0x02080100), unchecked((int) 0x02000000), unchecked((int) 0x42000000), unchecked((int) 0x00080100), + unchecked((int) 0x00080000), unchecked((int) 0x42000100), unchecked((int) 0x00000100), unchecked((int) 0x02000000), + unchecked((int) 0x40000000), unchecked((int) 0x02080000), unchecked((int) 0x42000100), unchecked((int) 0x40080100), + unchecked((int) 0x02000100), unchecked((int) 0x40000000), unchecked((int) 0x42080000), unchecked((int) 0x02080100), + unchecked((int) 0x40080100), unchecked((int) 0x00000100), unchecked((int) 0x02000000), unchecked((int) 0x42080000), + unchecked((int) 0x42080100), unchecked((int) 0x00080100), unchecked((int) 0x42000000), unchecked((int) 0x42080100), + unchecked((int) 0x02080000), unchecked((int) 0x00000000), unchecked((int) 0x40080000), unchecked((int) 0x42000000), + unchecked((int) 0x00080100), unchecked((int) 0x02000100), unchecked((int) 0x40000100), unchecked((int) 0x00080000), + unchecked((int) 0x00000000), unchecked((int) 0x40080000), unchecked((int) 0x02080100), unchecked((int) 0x40000100) + }; + + private static readonly int[] SP6 = + { + unchecked((int) 0x20000010), unchecked((int) 0x20400000), unchecked((int) 0x00004000), unchecked((int) 0x20404010), + unchecked((int) 0x20400000), unchecked((int) 0x00000010), unchecked((int) 0x20404010), unchecked((int) 0x00400000), + unchecked((int) 0x20004000), unchecked((int) 0x00404010), unchecked((int) 0x00400000), unchecked((int) 0x20000010), + unchecked((int) 0x00400010), unchecked((int) 0x20004000), unchecked((int) 0x20000000), unchecked((int) 0x00004010), + unchecked((int) 0x00000000), unchecked((int) 0x00400010), unchecked((int) 0x20004010), unchecked((int) 0x00004000), + unchecked((int) 0x00404000), unchecked((int) 0x20004010), unchecked((int) 0x00000010), unchecked((int) 0x20400010), + unchecked((int) 0x20400010), unchecked((int) 0x00000000), unchecked((int) 0x00404010), unchecked((int) 0x20404000), + unchecked((int) 0x00004010), unchecked((int) 0x00404000), unchecked((int) 0x20404000), unchecked((int) 0x20000000), + unchecked((int) 0x20004000), unchecked((int) 0x00000010), unchecked((int) 0x20400010), unchecked((int) 0x00404000), + unchecked((int) 0x20404010), unchecked((int) 0x00400000), unchecked((int) 0x00004010), unchecked((int) 0x20000010), + unchecked((int) 0x00400000), unchecked((int) 0x20004000), unchecked((int) 0x20000000), unchecked((int) 0x00004010), + unchecked((int) 0x20000010), unchecked((int) 0x20404010), unchecked((int) 0x00404000), unchecked((int) 0x20400000), + unchecked((int) 0x00404010), unchecked((int) 0x20404000), unchecked((int) 0x00000000), unchecked((int) 0x20400010), + unchecked((int) 0x00000010), unchecked((int) 0x00004000), unchecked((int) 0x20400000), unchecked((int) 0x00404010), + unchecked((int) 0x00004000), unchecked((int) 0x00400010), unchecked((int) 0x20004010), unchecked((int) 0x00000000), + unchecked((int) 0x20404000), unchecked((int) 0x20000000), unchecked((int) 0x00400010), unchecked((int) 0x20004010) + }; + + private static readonly int[] SP7 = + { + unchecked((int) 0x00200000), unchecked((int) 0x04200002), unchecked((int) 0x04000802), unchecked((int) 0x00000000), + unchecked((int) 0x00000800), unchecked((int) 0x04000802), unchecked((int) 0x00200802), unchecked((int) 0x04200800), + unchecked((int) 0x04200802), unchecked((int) 0x00200000), unchecked((int) 0x00000000), unchecked((int) 0x04000002), + unchecked((int) 0x00000002), unchecked((int) 0x04000000), unchecked((int) 0x04200002), unchecked((int) 0x00000802), + unchecked((int) 0x04000800), unchecked((int) 0x00200802), unchecked((int) 0x00200002), unchecked((int) 0x04000800), + unchecked((int) 0x04000002), unchecked((int) 0x04200000), unchecked((int) 0x04200800), unchecked((int) 0x00200002), + unchecked((int) 0x04200000), unchecked((int) 0x00000800), unchecked((int) 0x00000802), unchecked((int) 0x04200802), + unchecked((int) 0x00200800), unchecked((int) 0x00000002), unchecked((int) 0x04000000), unchecked((int) 0x00200800), + unchecked((int) 0x04000000), unchecked((int) 0x00200800), unchecked((int) 0x00200000), unchecked((int) 0x04000802), + unchecked((int) 0x04000802), unchecked((int) 0x04200002), unchecked((int) 0x04200002), unchecked((int) 0x00000002), + unchecked((int) 0x00200002), unchecked((int) 0x04000000), unchecked((int) 0x04000800), unchecked((int) 0x00200000), + unchecked((int) 0x04200800), unchecked((int) 0x00000802), unchecked((int) 0x00200802), unchecked((int) 0x04200800), + unchecked((int) 0x00000802), unchecked((int) 0x04000002), unchecked((int) 0x04200802), unchecked((int) 0x04200000), + unchecked((int) 0x00200800), unchecked((int) 0x00000000), unchecked((int) 0x00000002), unchecked((int) 0x04200802), + unchecked((int) 0x00000000), unchecked((int) 0x00200802), unchecked((int) 0x04200000), unchecked((int) 0x00000800), + unchecked((int) 0x04000002), unchecked((int) 0x04000800), unchecked((int) 0x00000800), unchecked((int) 0x00200002) + }; + + private static readonly int[] SP8 = + { + unchecked((int) 0x10001040), unchecked((int) 0x00001000), unchecked((int) 0x00040000), unchecked((int) 0x10041040), + unchecked((int) 0x10000000), unchecked((int) 0x10001040), unchecked((int) 0x00000040), unchecked((int) 0x10000000), + unchecked((int) 0x00040040), unchecked((int) 0x10040000), unchecked((int) 0x10041040), unchecked((int) 0x00041000), + unchecked((int) 0x10041000), unchecked((int) 0x00041040), unchecked((int) 0x00001000), unchecked((int) 0x00000040), + unchecked((int) 0x10040000), unchecked((int) 0x10000040), unchecked((int) 0x10001000), unchecked((int) 0x00001040), + unchecked((int) 0x00041000), unchecked((int) 0x00040040), unchecked((int) 0x10040040), unchecked((int) 0x10041000), + unchecked((int) 0x00001040), unchecked((int) 0x00000000), unchecked((int) 0x00000000), unchecked((int) 0x10040040), + unchecked((int) 0x10000040), unchecked((int) 0x10001000), unchecked((int) 0x00041040), unchecked((int) 0x00040000), + unchecked((int) 0x00041040), unchecked((int) 0x00040000), unchecked((int) 0x10041000), unchecked((int) 0x00001000), + unchecked((int) 0x00000040), unchecked((int) 0x10040040), unchecked((int) 0x00001000), unchecked((int) 0x00041040), + unchecked((int) 0x10001000), unchecked((int) 0x00000040), unchecked((int) 0x10000040), unchecked((int) 0x10040000), + unchecked((int) 0x10040040), unchecked((int) 0x10000000), unchecked((int) 0x00040000), unchecked((int) 0x10001040), + unchecked((int) 0x00000000), unchecked((int) 0x10041040), unchecked((int) 0x00040040), unchecked((int) 0x10000040), + unchecked((int) 0x10040000), unchecked((int) 0x10001000), unchecked((int) 0x10001040), unchecked((int) 0x00000000), + unchecked((int) 0x10041040), unchecked((int) 0x00041000), unchecked((int) 0x00041000), unchecked((int) 0x00001040), + unchecked((int) 0x00001040), unchecked((int) 0x00040040), unchecked((int) 0x10000000), unchecked((int) 0x10041000) + }; + + /** + * 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 and Phil Karn. + * (whoever, and wherever they are!). + */ + protected static int[] GenerateWorkingKey( + bool encrypting, + byte[] key) + { + int[] newKey = new int[32]; + bool[] pc1m = new bool[56]; + bool[] pcr = new bool[56]; + + for (int j = 0; j < 56; j++ ) + { + int l = pc1[j]; + + pc1m[j] = ((key[(uint) l >> 3] & bytebit[l & 07]) != 0); + } + + for (int i = 0; i < 16; i++) + { + int l, m, n; + + if (encrypting) + { + m = i << 1; + } + else + { + m = (15 - i) << 1; + } + + n = m + 1; + newKey[m] = newKey[n] = 0; + + for (int j = 0; j < 28; j++) + { + l = j + totrot[i]; + if ( l < 28 ) + { + pcr[j] = pc1m[l]; + } + else + { + pcr[j] = pc1m[l - 28]; + } + } + + for (int j = 28; j < 56; j++) + { + l = j + totrot[i]; + if (l < 56 ) + { + pcr[j] = pc1m[l]; + } + else + { + pcr[j] = pc1m[l - 28]; + } + } + + for (int j = 0; j < 24; j++) + { + if (pcr[pc2[j]]) + { + newKey[m] |= bigbyte[j]; + } + + if (pcr[pc2[j + 24]]) + { + newKey[n] |= bigbyte[j]; + } + } + } + + // + // store the processed key + // + for (int i = 0; i != 32; i += 2) + { + int i1, i2; + + i1 = newKey[i]; + i2 = newKey[i + 1]; + + newKey[i] = (int) ( (uint) ((i1 & 0x00fc0000) << 6) | + (uint) ((i1 & 0x00000fc0) << 10) | + ((uint) (i2 & 0x00fc0000) >> 10) | + ((uint) (i2 & 0x00000fc0) >> 6)); + + newKey[i + 1] = (int) ( (uint) ((i1 & 0x0003f000) << 12) | + (uint) ((i1 & 0x0000003f) << 16) | + ((uint) (i2 & 0x0003f000) >> 4) | + (uint) (i2 & 0x0000003f)); + } + + return newKey; + } + + /** + * the DES engine. + */ + internal static void DesFunc( + int[] wKey, + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int work, right, left; + + left = (input[inOff + 0] & 0xff) << 24; + left |= (input[inOff + 1] & 0xff) << 16; + left |= (input[inOff + 2] & 0xff) << 8; + left |= (input[inOff + 3] & 0xff); + + right = (input[inOff + 4] & 0xff) << 24; + right |= (input[inOff + 5] & 0xff) << 16; + right |= (input[inOff + 6] & 0xff) << 8; + right |= (input[inOff + 7] & 0xff); + + work = (int) (((uint) left >> 4) ^ right) & unchecked((int) 0x0f0f0f0f); + right ^= work; + left ^= (work << 4); + work = (int) (((uint) left >> 16) ^ right) & unchecked((int) 0x0000ffff); + right ^= work; + left ^= (work << 16); + work = (int) (((uint) right >> 2) ^ left) & unchecked((int) 0x33333333); + left ^= work; + right ^= (work << 2); + work = (int) (((uint) right >> 8) ^ left) & unchecked((int) 0x00ff00ff); + left ^= work; + right ^= (work << 8); + right = (int) ( (uint) (right << 1) | + ( ((uint) right >> 31) & 1 ) + ) & + unchecked((int) 0xffffffff); + work = (left ^ right) & unchecked((int) 0xaaaaaaaa); + left ^= work; + right ^= work; + left = (int) ( (uint) (left << 1) | + ( ((uint) left >> 31) & 1)) & + unchecked((int) 0xffffffff); + + for (int round = 0; round < 8; round++) + { + int fval; + + work = (int) ((uint) (right << 28) | ((uint) right >> 4)); + work ^= wKey[round * 4 + 0]; + fval = SP7[ work & 0x3f]; + fval |= SP5[((uint) work >> 8) & 0x3f]; + fval |= SP3[((uint) work >> 16) & 0x3f]; + fval |= SP1[((uint) work >> 24) & 0x3f]; + work = right ^ wKey[round * 4 + 1]; + fval |= SP8[ work & 0x3f]; + fval |= SP6[((uint) work >> 8) & 0x3f]; + fval |= SP4[((uint) work >> 16) & 0x3f]; + fval |= SP2[((uint) work >> 24) & 0x3f]; + left ^= fval; + work = (int) ((uint) (left << 28) | ((uint) left >> 4)); + work ^= wKey[round * 4 + 2]; + fval = SP7[ work & 0x3f]; + fval |= SP5[((uint) work >> 8) & 0x3f]; + fval |= SP3[((uint) work >> 16) & 0x3f]; + fval |= SP1[((uint) work >> 24) & 0x3f]; + work = left ^ wKey[round * 4 + 3]; + fval |= SP8[ work & 0x3f]; + fval |= SP6[((uint) work >> 8) & 0x3f]; + fval |= SP4[((uint) work >> 16) & 0x3f]; + fval |= SP2[((uint) work >> 24) & 0x3f]; + right ^= fval; + } + + right = (int) ((uint) (right << 31) | ((uint) right >> 1)); + work = (left ^ right) & unchecked((int) 0xaaaaaaaa); + left ^= work; + right ^= work; + left = (int) ((uint) (left << 31) | ((uint) left >> 1)); + work = (int) ((((uint) left >> 8) ^ right) & 0x00ff00ff); + right ^= work; + left ^= (work << 8); + work = (int) ((((uint) left >> 2) ^ right) & 0x33333333); + right ^= work; + left ^= (work << 2); + work = (int) ((((uint) right >> 16) ^ left) & 0x0000ffff); + left ^= work; + right ^= (work << 16); + work = (int) ((((uint) right >> 4) ^ left) & 0x0f0f0f0f); + left ^= work; + right ^= (work << 4); + + outBytes[outOff + 0] = (byte)(((uint) right >> 24) & 0xff); + outBytes[outOff + 1] = (byte)(((uint) right >> 16) & 0xff); + outBytes[outOff + 2] = (byte)(((uint) right >> 8) & 0xff); + outBytes[outOff + 3] = (byte)( right & 0xff); + outBytes[outOff + 4] = (byte)(((uint) left >> 24) & 0xff); + outBytes[outOff + 5] = (byte)(((uint) left >> 16) & 0xff); + outBytes[outOff + 6] = (byte)(((uint) left >> 8) & 0xff); + outBytes[outOff + 7] = (byte)( left & 0xff); + } + } +} diff --git a/src/core/srcbc/crypto/engines/ElGamalEngine.cs b/src/core/srcbc/crypto/engines/ElGamalEngine.cs new file mode 100644 index 0000000..012f4af --- /dev/null +++ b/src/core/srcbc/crypto/engines/ElGamalEngine.cs @@ -0,0 +1,178 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * this does your basic ElGamal algorithm. + */ + public class ElGamalEngine + : IAsymmetricBlockCipher + { + private ElGamalKeyParameters key; + private SecureRandom random; + private bool forEncryption; + private int bitSize; + + public string AlgorithmName + { + get { return "ElGamal"; } + } + + /** + * initialise the ElGamal engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary ElGamal key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom) parameters; + + this.key = (ElGamalKeyParameters) p.Parameters; + this.random = p.Random; + } + else + { + this.key = (ElGamalKeyParameters) parameters; + this.random = new SecureRandom(); + } + + this.forEncryption = forEncryption; + this.bitSize = key.Parameters.P.BitLength; + + if (forEncryption) + { + if (!(key is ElGamalPublicKeyParameters)) + { + throw new ArgumentException("ElGamalPublicKeyParameters are required for encryption."); + } + } + else + { + if (!(key is ElGamalPrivateKeyParameters)) + { + throw new ArgumentException("ElGamalPrivateKeyParameters are required for decryption."); + } + } + } + + /** + * Return the maximum size for an input block to this engine. + * For ElGamal this is always one byte less than the size of P on + * encryption, and twice the length as the size of P on decryption. + * + * @return maximum size for an input block. + */ + public int GetInputBlockSize() + { + if (forEncryption) + { + return (bitSize - 1) / 8; + } + + return 2 * ((bitSize + 7) / 8); + } + + /** + * Return the maximum size for an output block to this engine. + * For ElGamal this is always one byte less than the size of P on + * decryption, and twice the length as the size of P on encryption. + * + * @return maximum size for an output block. + */ + public int GetOutputBlockSize() + { + if (forEncryption) + { + return 2 * ((bitSize + 7) / 8); + } + + return (bitSize - 1) / 8; + } + + /** + * Process a single block using the basic ElGamal algorithm. + * + * @param in the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param length the length of the data to be processed. + * @return the result of the ElGamal process. + * @exception DataLengthException the input block is too large. + */ + public byte[] ProcessBlock( + byte[] input, + int inOff, + int length) + { + if (key == null) + throw new InvalidOperationException("ElGamal engine not initialised"); + + int maxLength = forEncryption + ? (bitSize - 1 + 7) / 8 + : GetInputBlockSize(); + + if (length > maxLength) + throw new DataLengthException("input too large for ElGamal cipher.\n"); + + BigInteger p = key.Parameters.P; + + byte[] output; + if (key is ElGamalPrivateKeyParameters) // decryption + { + int halfLength = length / 2; + BigInteger gamma = new BigInteger(1, input, inOff, halfLength); + BigInteger phi = new BigInteger(1, input, inOff + halfLength, halfLength); + + ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters) key; + + // a shortcut, which generally relies on p being prime amongst other things. + // if a problem with this shows up, check the p and g values! + BigInteger m = gamma.ModPow(p.Subtract(BigInteger.One).Subtract(priv.X), p).Multiply(phi).Mod(p); + + output = m.ToByteArrayUnsigned(); + } + else // encryption + { + BigInteger tmp = new BigInteger(1, input, inOff, length); + + if (tmp.BitLength >= p.BitLength) + throw new DataLengthException("input too large for ElGamal cipher.\n"); + + + ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters) key; + + BigInteger pSub2 = p.Subtract(BigInteger.Two); + + // TODO In theory, a series of 'k', 'g.ModPow(k, p)' and 'y.ModPow(k, p)' can be pre-calculated + BigInteger k; + do + { + k = new BigInteger(p.BitLength, random); + } + while (k.SignValue == 0 || k.CompareTo(pSub2) > 0); + + BigInteger g = key.Parameters.G; + BigInteger gamma = g.ModPow(k, p); + BigInteger phi = tmp.Multiply(pub.Y.ModPow(k, p)).Mod(p); + + output = new byte[this.GetOutputBlockSize()]; + + // TODO Add methods to allow writing BigInteger to existing byte array? + byte[] out1 = gamma.ToByteArrayUnsigned(); + byte[] out2 = phi.ToByteArrayUnsigned(); + out1.CopyTo(output, output.Length / 2 - out1.Length); + out2.CopyTo(output, output.Length - out2.Length); + } + + return output; + } + } +} diff --git a/src/core/srcbc/crypto/engines/GOST28147Engine.cs b/src/core/srcbc/crypto/engines/GOST28147Engine.cs new file mode 100644 index 0000000..585db4a --- /dev/null +++ b/src/core/srcbc/crypto/engines/GOST28147Engine.cs @@ -0,0 +1,364 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * implementation of GOST 28147-89 + */ + public class Gost28147Engine + : IBlockCipher + { + private const int BlockSize = 8; + private int[] workingKey = null; + private bool forEncryption; + + // these are the S-boxes given in Applied Cryptography 2nd Ed., p. 333 + // This is default S-box! + private readonly byte[] S = { + 0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3, + 0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9, + 0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB, + 0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3, + 0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2, + 0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE, + 0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC, + 0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC + }; + + /* + * class content S-box parameters for encrypting + * getting from, see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt + * http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-02.txt + */ + private static readonly byte[] ESbox_Test = { + 0x4,0x2,0xF,0x5,0x9,0x1,0x0,0x8,0xE,0x3,0xB,0xC,0xD,0x7,0xA,0x6, + 0xC,0x9,0xF,0xE,0x8,0x1,0x3,0xA,0x2,0x7,0x4,0xD,0x6,0x0,0xB,0x5, + 0xD,0x8,0xE,0xC,0x7,0x3,0x9,0xA,0x1,0x5,0x2,0x4,0x6,0xF,0x0,0xB, + 0xE,0x9,0xB,0x2,0x5,0xF,0x7,0x1,0x0,0xD,0xC,0x6,0xA,0x4,0x3,0x8, + 0x3,0xE,0x5,0x9,0x6,0x8,0x0,0xD,0xA,0xB,0x7,0xC,0x2,0x1,0xF,0x4, + 0x8,0xF,0x6,0xB,0x1,0x9,0xC,0x5,0xD,0x3,0x7,0xA,0x0,0xE,0x2,0x4, + 0x9,0xB,0xC,0x0,0x3,0x6,0x7,0x5,0x4,0x8,0xE,0xF,0x1,0xA,0x2,0xD, + 0xC,0x6,0x5,0x2,0xB,0x0,0x9,0xD,0x3,0xE,0x7,0xA,0xF,0x4,0x1,0x8 + }; + + private static readonly byte[] ESbox_A = { + 0x9,0x6,0x3,0x2,0x8,0xB,0x1,0x7,0xA,0x4,0xE,0xF,0xC,0x0,0xD,0x5, + 0x3,0x7,0xE,0x9,0x8,0xA,0xF,0x0,0x5,0x2,0x6,0xC,0xB,0x4,0xD,0x1, + 0xE,0x4,0x6,0x2,0xB,0x3,0xD,0x8,0xC,0xF,0x5,0xA,0x0,0x7,0x1,0x9, + 0xE,0x7,0xA,0xC,0xD,0x1,0x3,0x9,0x0,0x2,0xB,0x4,0xF,0x8,0x5,0x6, + 0xB,0x5,0x1,0x9,0x8,0xD,0xF,0x0,0xE,0x4,0x2,0x3,0xC,0x7,0xA,0x6, + 0x3,0xA,0xD,0xC,0x1,0x2,0x0,0xB,0x7,0x5,0x9,0x4,0x8,0xF,0xE,0x6, + 0x1,0xD,0x2,0x9,0x7,0xA,0x6,0x0,0x8,0xC,0x4,0x5,0xF,0x3,0xB,0xE, + 0xB,0xA,0xF,0x5,0x0,0xC,0xE,0x8,0x6,0x2,0x3,0x9,0x1,0x7,0xD,0x4 + }; + + private static readonly byte[] ESbox_B = { + 0x8,0x4,0xB,0x1,0x3,0x5,0x0,0x9,0x2,0xE,0xA,0xC,0xD,0x6,0x7,0xF, + 0x0,0x1,0x2,0xA,0x4,0xD,0x5,0xC,0x9,0x7,0x3,0xF,0xB,0x8,0x6,0xE, + 0xE,0xC,0x0,0xA,0x9,0x2,0xD,0xB,0x7,0x5,0x8,0xF,0x3,0x6,0x1,0x4, + 0x7,0x5,0x0,0xD,0xB,0x6,0x1,0x2,0x3,0xA,0xC,0xF,0x4,0xE,0x9,0x8, + 0x2,0x7,0xC,0xF,0x9,0x5,0xA,0xB,0x1,0x4,0x0,0xD,0x6,0x8,0xE,0x3, + 0x8,0x3,0x2,0x6,0x4,0xD,0xE,0xB,0xC,0x1,0x7,0xF,0xA,0x0,0x9,0x5, + 0x5,0x2,0xA,0xB,0x9,0x1,0xC,0x3,0x7,0x4,0xD,0x0,0x6,0xF,0x8,0xE, + 0x0,0x4,0xB,0xE,0x8,0x3,0x7,0x1,0xA,0x2,0x9,0x6,0xF,0xD,0x5,0xC + }; + + private static readonly byte[] ESbox_C = { + 0x1,0xB,0xC,0x2,0x9,0xD,0x0,0xF,0x4,0x5,0x8,0xE,0xA,0x7,0x6,0x3, + 0x0,0x1,0x7,0xD,0xB,0x4,0x5,0x2,0x8,0xE,0xF,0xC,0x9,0xA,0x6,0x3, + 0x8,0x2,0x5,0x0,0x4,0x9,0xF,0xA,0x3,0x7,0xC,0xD,0x6,0xE,0x1,0xB, + 0x3,0x6,0x0,0x1,0x5,0xD,0xA,0x8,0xB,0x2,0x9,0x7,0xE,0xF,0xC,0x4, + 0x8,0xD,0xB,0x0,0x4,0x5,0x1,0x2,0x9,0x3,0xC,0xE,0x6,0xF,0xA,0x7, + 0xC,0x9,0xB,0x1,0x8,0xE,0x2,0x4,0x7,0x3,0x6,0x5,0xA,0x0,0xF,0xD, + 0xA,0x9,0x6,0x8,0xD,0xE,0x2,0x0,0xF,0x3,0x5,0xB,0x4,0x1,0xC,0x7, + 0x7,0x4,0x0,0x5,0xA,0x2,0xF,0xE,0xC,0x6,0x1,0xB,0xD,0x9,0x3,0x8 + }; + + private static readonly byte[] ESbox_D = { + 0xF,0xC,0x2,0xA,0x6,0x4,0x5,0x0,0x7,0x9,0xE,0xD,0x1,0xB,0x8,0x3, + 0xB,0x6,0x3,0x4,0xC,0xF,0xE,0x2,0x7,0xD,0x8,0x0,0x5,0xA,0x9,0x1, + 0x1,0xC,0xB,0x0,0xF,0xE,0x6,0x5,0xA,0xD,0x4,0x8,0x9,0x3,0x7,0x2, + 0x1,0x5,0xE,0xC,0xA,0x7,0x0,0xD,0x6,0x2,0xB,0x4,0x9,0x3,0xF,0x8, + 0x0,0xC,0x8,0x9,0xD,0x2,0xA,0xB,0x7,0x3,0x6,0x5,0x4,0xE,0xF,0x1, + 0x8,0x0,0xF,0x3,0x2,0x5,0xE,0xB,0x1,0xA,0x4,0x7,0xC,0x9,0xD,0x6, + 0x3,0x0,0x6,0xF,0x1,0xE,0x9,0x2,0xD,0x8,0xC,0x4,0xB,0xA,0x5,0x7, + 0x1,0xA,0x6,0x8,0xF,0xB,0x0,0x4,0xC,0x3,0x5,0x9,0x7,0xD,0x2,0xE + }; + + //S-box for digest + private static readonly byte[] DSbox_Test = { + 0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3, + 0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9, + 0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB, + 0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3, + 0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2, + 0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE, + 0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC, + 0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC + }; + + private static readonly byte[] DSbox_A = { + 0xA,0x4,0x5,0x6,0x8,0x1,0x3,0x7,0xD,0xC,0xE,0x0,0x9,0x2,0xB,0xF, + 0x5,0xF,0x4,0x0,0x2,0xD,0xB,0x9,0x1,0x7,0x6,0x3,0xC,0xE,0xA,0x8, + 0x7,0xF,0xC,0xE,0x9,0x4,0x1,0x0,0x3,0xB,0x5,0x2,0x6,0xA,0x8,0xD, + 0x4,0xA,0x7,0xC,0x0,0xF,0x2,0x8,0xE,0x1,0x6,0x5,0xD,0xB,0x9,0x3, + 0x7,0x6,0x4,0xB,0x9,0xC,0x2,0xA,0x1,0x8,0x0,0xE,0xF,0xD,0x3,0x5, + 0x7,0x6,0x2,0x4,0xD,0x9,0xF,0x0,0xA,0x1,0x5,0xB,0x8,0xE,0xC,0x3, + 0xD,0xE,0x4,0x1,0x7,0x0,0x5,0xA,0x3,0xC,0x8,0xF,0x6,0x2,0x9,0xB, + 0x1,0x3,0xA,0x9,0x5,0xB,0x4,0xF,0x8,0x6,0x7,0xE,0xD,0x0,0x2,0xC + }; + + // + // pre-defined sbox table + // + private static readonly Hashtable sBoxes = new Hashtable(); + + static Gost28147Engine() + { + sBoxes.Add("E-TEST", ESbox_Test); + sBoxes.Add("E-A", ESbox_A); + sBoxes.Add("E-B", ESbox_B); + sBoxes.Add("E-C", ESbox_C); + sBoxes.Add("E-D", ESbox_D); + sBoxes.Add("D-TEST", DSbox_Test); + sBoxes.Add("D-A", DSbox_A); + } + + /** + * standard constructor. + */ + public Gost28147Engine() + { + } + + /** + * initialise an Gost28147 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithSBox) + { + ParametersWithSBox param = (ParametersWithSBox)parameters; + + // + // Set the S-Box + // + Array.Copy(param.GetSBox(), 0, this.S, 0, param.GetSBox().Length); + + // + // set key if there is one + // + if (param.Parameters != null) + { + workingKey = generateWorkingKey(forEncryption, + ((KeyParameter)param.Parameters).GetKey()); + } + } + else if (parameters is KeyParameter) + { + workingKey = generateWorkingKey(forEncryption, + ((KeyParameter)parameters).GetKey()); + } + else + { + throw new ArgumentException("invalid parameter passed to Gost28147 init - " + parameters.GetType().Name); + } + } + + public string AlgorithmName + { + get { return "Gost28147"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BlockSize; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + { + throw new InvalidOperationException("Gost28147 engine not initialised"); + } + + if ((inOff + BlockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + BlockSize) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + Gost28147Func(workingKey, input, inOff, output, outOff); + + return BlockSize; + } + + public void Reset() + { + } + + private int[] generateWorkingKey( + bool forEncryption, + byte[] userKey) + { + this.forEncryption = forEncryption; + + if (userKey.Length != 32) + { + throw new ArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!"); + } + + int[] key = new int[8]; + for(int i=0; i!=8; i++) + { + key[i] = bytesToint(userKey,i*4); + } + + return key; + } + + private int Gost28147_mainStep(int n1, int key) + { + int cm = (key + n1); // CM1 + + // S-box replacing + + int om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4); + om += S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4); + om += S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4); + om += S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4); + om += S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4); + om += S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4); + om += S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4); + om += S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4); + +// return om << 11 | om >>> (32-11); // 11-leftshift + int omLeft = om << 11; + int omRight = (int)(((uint) om) >> (32 - 11)); // Note: Casts required to get unsigned bit rotation + + return omLeft | omRight; + } + + private void Gost28147Func( + int[] workingKey, + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + int N1, N2, tmp; //tmp -> for saving N1 + N1 = bytesToint(inBytes, inOff); + N2 = bytesToint(inBytes, inOff + 4); + + if (this.forEncryption) + { + for(int k = 0; k < 3; k++) // 1-24 steps + { + for(int j = 0; j < 8; j++) + { + tmp = N1; + int step = Gost28147_mainStep(N1, workingKey[j]); + N1 = N2 ^ step; // CM2 + N2 = tmp; + } + } + for(int j = 7; j > 0; j--) // 25-31 steps + { + tmp = N1; + N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2 + N2 = tmp; + } + } + else //decrypt + { + for(int j = 0; j < 8; j++) // 1-8 steps + { + tmp = N1; + N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2 + N2 = tmp; + } + for(int k = 0; k < 3; k++) //9-31 steps + { + for(int j = 7; j >= 0; j--) + { + if ((k == 2) && (j==0)) + { + break; // break 32 step + } + tmp = N1; + N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2 + N2 = tmp; + } + } + } + + N2 = N2 ^ Gost28147_mainStep(N1, workingKey[0]); // 32 step (N1=N1) + + intTobytes(N1, outBytes, outOff); + intTobytes(N2, outBytes, outOff + 4); + } + + //array of bytes to type int + private static int bytesToint( + byte[] inBytes, + int inOff) + { + return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) + + ((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff); + } + + //int to array of bytes + private static void intTobytes( + int num, + byte[] outBytes, + int outOff) + { + outBytes[outOff + 3] = (byte)(num >> 24); + outBytes[outOff + 2] = (byte)(num >> 16); + outBytes[outOff + 1] = (byte)(num >> 8); + outBytes[outOff] = (byte)num; + } + + /** + * Return the S-Box associated with SBoxName + * @param sBoxName name of the S-Box + * @return byte array representing the S-Box + */ + public static byte[] GetSBox( + string sBoxName) + { + byte[] namedSBox = (byte[])sBoxes[sBoxName.ToUpper(CultureInfo.InvariantCulture)]; + + if (namedSBox == null) + { + throw new ArgumentException("Unknown S-Box - possible types: " + + "\"E-Test\", \"E-A\", \"E-B\", \"E-C\", \"E-D\", \"D-Test\", \"D-A\"."); + } + + return (byte[]) namedSBox.Clone(); + } + } +} diff --git a/src/core/srcbc/crypto/engines/HC128Engine.cs b/src/core/srcbc/crypto/engines/HC128Engine.cs new file mode 100644 index 0000000..0a8d214 --- /dev/null +++ b/src/core/srcbc/crypto/engines/HC128Engine.cs @@ -0,0 +1,241 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * HC-128 is a software-efficient stream cipher created by Hongjun Wu. It + * generates keystream from a 128-bit secret key and a 128-bit initialization + * vector. + *

+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf + *

+ * It is a third phase candidate in the eStream contest, and is patent-free. + * No attacks are known as of today (April 2007). See + * + * http://www.ecrypt.eu.org/stream/hcp3.html + *

+ */ + public class HC128Engine + : IStreamCipher + { + private uint[] p = new uint[512]; + private uint[] q = new uint[512]; + private uint cnt = 0; + + private static uint F1(uint x) + { + return RotateRight(x, 7) ^ RotateRight(x, 18) ^ (x >> 3); + } + + private static uint F2(uint x) + { + return RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10); + } + + private uint G1(uint x, uint y, uint z) + { + return (RotateRight(x, 10) ^ RotateRight(z, 23)) + RotateRight(y, 8); + } + + private uint G2(uint x, uint y, uint z) + { + return (RotateLeft(x, 10) ^ RotateLeft(z, 23)) + RotateLeft(y, 8); + } + + private static uint RotateLeft(uint x, int bits) + { + return (x << bits) | (x >> -bits); + } + + private static uint RotateRight(uint x, int bits) + { + return (x >> bits) | (x << -bits); + } + + private uint H1(uint x) + { + return q[x & 0xFF] + q[((x >> 16) & 0xFF) + 256]; + } + + private uint H2(uint x) + { + return p[x & 0xFF] + p[((x >> 16) & 0xFF) + 256]; + } + + private static uint Mod1024(uint x) + { + return x & 0x3FF; + } + + private static uint Mod512(uint x) + { + return x & 0x1FF; + } + + private static uint Dim(uint x, uint y) + { + return Mod512(x - y); + } + + private uint Step() + { + uint j = Mod512(cnt); + uint ret; + if (cnt < 512) + { + p[j] += G1(p[Dim(j, 3)], p[Dim(j, 10)], p[Dim(j, 511)]); + ret = H1(p[Dim(j, 12)]) ^ p[j]; + } + else + { + q[j] += G2(q[Dim(j, 3)], q[Dim(j, 10)], q[Dim(j, 511)]); + ret = H2(q[Dim(j, 12)]) ^ q[j]; + } + cnt = Mod1024(cnt + 1); + return ret; + } + + private byte[] key, iv; + private bool initialised; + + private void Init() + { + if (key.Length != 16) + throw new ArgumentException("The key must be 128 bit long"); + + cnt = 0; + + uint[] w = new uint[1280]; + + for (int i = 0; i < 16; i++) + { + w[i >> 3] |= ((uint)key[i] << (i & 0x7)); + } + Array.Copy(w, 0, w, 4, 4); + + for (int i = 0; i < iv.Length && i < 16; i++) + { + w[(i >> 3) + 8] |= ((uint)iv[i] << (i & 0x7)); + } + Array.Copy(w, 8, w, 12, 4); + + for (uint i = 16; i < 1280; i++) + { + w[i] = F2(w[i - 2]) + w[i - 7] + F1(w[i - 15]) + w[i - 16] + i; + } + + Array.Copy(w, 256, p, 0, 512); + Array.Copy(w, 768, q, 0, 512); + + for (int i = 0; i < 512; i++) + { + p[i] = Step(); + } + for (int i = 0; i < 512; i++) + { + q[i] = Step(); + } + + cnt = 0; + } + + public string AlgorithmName + { + get { return "HC-128"; } + } + + /** + * Initialise a HC-128 cipher. + * + * @param forEncryption whether or not we are for encryption. Irrelevant, as + * encryption and decryption are the same. + * @param params the parameters required to set up the cipher. + * @throws ArgumentException if the params argument is + * inappropriate (ie. the key is not 128 bit long). + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + ICipherParameters keyParam = parameters; + + if (parameters is ParametersWithIV) + { + iv = ((ParametersWithIV)parameters).GetIV(); + keyParam = ((ParametersWithIV)parameters).Parameters; + } + else + { + iv = new byte[0]; + } + + if (keyParam is KeyParameter) + { + key = ((KeyParameter)keyParam).GetKey(); + Init(); + } + else + { + throw new ArgumentException( + "Invalid parameter passed to HC128 init - " + parameters.GetType().Name, + "parameters"); + } + + initialised = true; + } + + private byte[] buf = new byte[4]; + private int idx = 0; + + private byte GetByte() + { + if (idx == 0) + { + uint step = Step(); + buf[3] = (byte)step; + step >>= 8; + buf[2] = (byte)step; + step >>= 8; + buf[1] = (byte)step; + step >>= 8; + buf[0] = (byte)step; + } + byte ret = buf[idx]; + idx = idx + 1 & 0x3; + return ret; + } + + public void ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + len) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + len) > output.Length) + throw new DataLengthException("output buffer too short"); + + for (int i = 0; i < len; i++) + { + output[outOff + i] = (byte)(input[inOff + i] ^ GetByte()); + } + } + + public void Reset() + { + idx = 0; + Init(); + } + + public byte ReturnByte(byte input) + { + return (byte)(input ^ GetByte()); + } + } +} diff --git a/src/core/srcbc/crypto/engines/HC256Engine.cs b/src/core/srcbc/crypto/engines/HC256Engine.cs new file mode 100644 index 0000000..eff3ebc --- /dev/null +++ b/src/core/srcbc/crypto/engines/HC256Engine.cs @@ -0,0 +1,207 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * HC-256 is a software-efficient stream cipher created by Hongjun Wu. It + * generates keystream from a 256-bit secret key and a 256-bit initialization + * vector. + *

+ * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf + *

+ * Its brother, HC-128, is a third phase candidate in the eStream contest. + * The algorithm is patent-free. No attacks are known as of today (April 2007). + * See + * + * http://www.ecrypt.eu.org/stream/hcp3.html + *

+ */ + public class HC256Engine + : IStreamCipher + { + private uint[] p = new uint[1024]; + private uint[] q = new uint[1024]; + private uint cnt = 0; + + private uint Step() + { + uint j = cnt & 0x3FF; + uint ret; + if (cnt < 1024) + { + uint x = p[(j - 3 & 0x3FF)]; + uint y = p[(j - 1023 & 0x3FF)]; + p[j] += p[(j - 10 & 0x3FF)] + + (RotateRight(x, 10) ^ RotateRight(y, 23)) + + q[((x ^ y) & 0x3FF)]; + + x = p[(j - 12 & 0x3FF)]; + ret = (q[x & 0xFF] + q[((x >> 8) & 0xFF) + 256] + + q[((x >> 16) & 0xFF) + 512] + q[((x >> 24) & 0xFF) + 768]) + ^ p[j]; + } + else + { + uint x = q[(j - 3 & 0x3FF)]; + uint y = q[(j - 1023 & 0x3FF)]; + q[j] += q[(j - 10 & 0x3FF)] + + (RotateRight(x, 10) ^ RotateRight(y, 23)) + + p[((x ^ y) & 0x3FF)]; + + x = q[(j - 12 & 0x3FF)]; + ret = (p[x & 0xFF] + p[((x >> 8) & 0xFF) + 256] + + p[((x >> 16) & 0xFF) + 512] + p[((x >> 24) & 0xFF) + 768]) + ^ q[j]; + } + cnt = cnt + 1 & 0x7FF; + return ret; + } + + private byte[] key, iv; + private bool initialised; + + private void Init() + { + if (key.Length != 32) + throw new ArgumentException("The key must be 256 bit long"); + + cnt = 0; + + uint[] w = new uint[2560]; + + for (int i = 0; i < 32; i++) + { + w[i >> 3] |= ((uint)key[i] << (i & 0x7)); + } + + for (int i = 0; i < iv.Length && i < 32; i++) + { + w[(i >> 3) + 8] |= ((uint)iv[i] << (i & 0x7)); + } + + for (uint i = 16; i < 2560; i++) + { + uint x = w[i - 2]; + uint y = w[i - 15]; + w[i] = (RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10)) + + w[i - 7] + + (RotateRight(y, 7) ^ RotateRight(y, 18) ^ (y >> 3)) + + w[i - 16] + i; + } + + Array.Copy(w, 512, p, 0, 1024); + Array.Copy(w, 1536, q, 0, 1024); + + for (int i = 0; i < 4096; i++) + { + Step(); + } + + cnt = 0; + } + + public string AlgorithmName + { + get { return "HC-256"; } + } + + /** + * Initialise a HC-256 cipher. + * + * @param forEncryption whether or not we are for encryption. Irrelevant, as + * encryption and decryption are the same. + * @param params the parameters required to set up the cipher. + * @throws ArgumentException if the params argument is + * inappropriate (ie. the key is not 256 bit long). + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + ICipherParameters keyParam = parameters; + + if (parameters is ParametersWithIV) + { + iv = ((ParametersWithIV)parameters).GetIV(); + keyParam = ((ParametersWithIV)parameters).Parameters; + } + else + { + iv = new byte[0]; + } + + if (keyParam is KeyParameter) + { + key = ((KeyParameter)keyParam).GetKey(); + Init(); + } + else + { + throw new ArgumentException( + "Invalid parameter passed to HC256 init - " + parameters.GetType().Name, + "parameters"); + } + + initialised = true; + } + + private byte[] buf = new byte[4]; + private int idx = 0; + + private byte GetByte() + { + if (idx == 0) + { + uint step = Step(); + buf[3] = (byte)step; + step >>= 8; + buf[2] = (byte)step; + step >>= 8; + buf[1] = (byte)step; + step >>= 8; + buf[0] = (byte)step; + } + byte ret = buf[idx]; + idx = idx + 1 & 0x3; + return ret; + } + + public void ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + len) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + len) > output.Length) + throw new DataLengthException("output buffer too short"); + + for (int i = 0; i < len; i++) + { + output[outOff + i] = (byte)(input[inOff + i] ^ GetByte()); + } + } + + public void Reset() + { + idx = 0; + Init(); + } + + public byte ReturnByte(byte input) + { + return (byte)(input ^ GetByte()); + } + + private static uint RotateRight(uint x, int bits) + { + return (x >> bits) | (x << -bits); + } + } +} diff --git a/src/core/srcbc/crypto/engines/ISAACEngine.cs b/src/core/srcbc/crypto/engines/ISAACEngine.cs new file mode 100644 index 0000000..50e9def --- /dev/null +++ b/src/core/srcbc/crypto/engines/ISAACEngine.cs @@ -0,0 +1,252 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Implementation of Bob Jenkin's ISAAC (Indirection Shift Accumulate Add and Count). + * see: http://www.burtleburtle.net/bob/rand/isaacafa.html + */ + public class IsaacEngine + : IStreamCipher + { + // Constants + private static readonly int sizeL = 8, + stateArraySize = sizeL<<5; // 256 + + // Cipher's internal state + private uint[] engineState = null, // mm + results = null; // randrsl + private uint a = 0, b = 0, c = 0; + + // Engine state + private int index = 0; + private byte[] keyStream = new byte[stateArraySize<<2], // results expanded into bytes + workingKey = null; + private bool initialised = false; + + /** + * initialise an ISAAC cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException( + "invalid parameter passed to ISAAC Init - " + parameters.GetType().Name, + "parameters"); + + /* + * ISAAC encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. + */ + KeyParameter p = (KeyParameter) parameters; + setKey(p.GetKey()); + } + + public byte ReturnByte( + byte input) + { + if (index == 0) + { + isaac(); + keyStream = intToByteLittle(results); + } + + byte output = (byte)(keyStream[index]^input); + index = (index + 1) & 1023; + + return output; + } + + public void ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + len) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + len) > output.Length) + throw new DataLengthException("output buffer too short"); + + for (int i = 0; i < len; i++) + { + if (index == 0) + { + isaac(); + keyStream = intToByteLittle(results); + } + output[i+outOff] = (byte)(keyStream[index]^input[i+inOff]); + index = (index + 1) & 1023; + } + } + + public string AlgorithmName + { + get { return "ISAAC"; } + } + + public void Reset() + { + setKey(workingKey); + } + + // Private implementation + private void setKey( + byte[] keyBytes) + { + workingKey = keyBytes; + + if (engineState == null) + { + engineState = new uint[stateArraySize]; + } + + if (results == null) + { + results = new uint[stateArraySize]; + } + + int i, j, k; + + // Reset state + for (i = 0; i < stateArraySize; i++) + { + engineState[i] = results[i] = 0; + } + a = b = c = 0; + + // Reset index counter for output + index = 0; + + // Convert the key bytes to ints and put them into results[] for initialization + byte[] t = new byte[keyBytes.Length + (keyBytes.Length & 3)]; + Array.Copy(keyBytes, 0, t, 0, keyBytes.Length); + for (i = 0; i < t.Length; i+=4) + { + results[i>>2] = byteToIntLittle(t, i); + } + + // It has begun? + uint[] abcdefgh = new uint[sizeL]; + + for (i = 0; i < sizeL; i++) + { + abcdefgh[i] = 0x9e3779b9; // Phi (golden ratio) + } + + for (i = 0; i < 4; i++) + { + mix(abcdefgh); + } + + for (i = 0; i < 2; i++) + { + for (j = 0; j < stateArraySize; j+=sizeL) + { + for (k = 0; k < sizeL; k++) + { + abcdefgh[k] += (i<1) ? results[j+k] : engineState[j+k]; + } + + mix(abcdefgh); + + for (k = 0; k < sizeL; k++) + { + engineState[j+k] = abcdefgh[k]; + } + } + } + + isaac(); + + initialised = true; + } + + private void isaac() + { + uint x, y; + + b += ++c; + for (int i = 0; i < stateArraySize; i++) + { + x = engineState[i]; + switch (i & 3) + { + case 0: a ^= (a << 13); break; + case 1: a ^= (a >> 6); break; + case 2: a ^= (a << 2); break; + case 3: a ^= (a >> 16); break; + } + a += engineState[(i+128) & 0xFF]; + engineState[i] = y = engineState[(int)((uint)x >> 2) & 0xFF] + a + b; + results[i] = b = engineState[(int)((uint)y >> 10) & 0xFF] + x; + } + } + + private void mix(uint[] x) + { +// x[0]^=x[1]<< 11; x[3]+=x[0]; x[1]+=x[2]; +// x[1]^=x[2]>>> 2; x[4]+=x[1]; x[2]+=x[3]; +// x[2]^=x[3]<< 8; x[5]+=x[2]; x[3]+=x[4]; +// x[3]^=x[4]>>>16; x[6]+=x[3]; x[4]+=x[5]; +// x[4]^=x[5]<< 10; x[7]+=x[4]; x[5]+=x[6]; +// x[5]^=x[6]>>> 4; x[0]+=x[5]; x[6]+=x[7]; +// x[6]^=x[7]<< 8; x[1]+=x[6]; x[7]+=x[0]; +// x[7]^=x[0]>>> 9; x[2]+=x[7]; x[0]+=x[1]; + x[0]^=x[1]<< 11; x[3]+=x[0]; x[1]+=x[2]; + x[1]^=x[2]>> 2; x[4]+=x[1]; x[2]+=x[3]; + x[2]^=x[3]<< 8; x[5]+=x[2]; x[3]+=x[4]; + x[3]^=x[4]>> 16; x[6]+=x[3]; x[4]+=x[5]; + x[4]^=x[5]<< 10; x[7]+=x[4]; x[5]+=x[6]; + x[5]^=x[6]>> 4; x[0]+=x[5]; x[6]+=x[7]; + x[6]^=x[7]<< 8; x[1]+=x[6]; x[7]+=x[0]; + x[7]^=x[0]>> 9; x[2]+=x[7]; x[0]+=x[1]; + } + + private uint byteToIntLittle( + byte[] x, + int offset) + { + uint result = (byte) x[offset + 3]; + result = (result << 8) | x[offset + 2]; + result = (result << 8) | x[offset + 1]; + result = (result << 8) | x[offset + 0]; + return result; + } + + private byte[] intToByteLittle( + uint x) + { + byte[] output = new byte[4]; + output[3] = (byte)x; + output[2] = (byte)(x >> 8); + output[1] = (byte)(x >> 16); + output[0] = (byte)(x >> 24); + return output; + } + + private byte[] intToByteLittle( + uint[] x) + { + byte[] output = new byte[4*x.Length]; + for (int i = 0, j = 0; i < x.Length; i++,j+=4) + { + Array.Copy(intToByteLittle(x[i]), 0, output, j, 4); + } + return output; + } + } +} diff --git a/src/core/srcbc/crypto/engines/IdeaEngine.cs b/src/core/srcbc/crypto/engines/IdeaEngine.cs new file mode 100644 index 0000000..247b91a --- /dev/null +++ b/src/core/srcbc/crypto/engines/IdeaEngine.cs @@ -0,0 +1,333 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * A class that provides a basic International Data Encryption Algorithm (IDEA) engine. + *

+ * 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 + * end of the mulinv function!). + *

+ *

+ * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/ + *

+ *

+ * Note: 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 + * www.mediacrypt.com for + * further details. This announcement has been included at the request of + * the patent holders. + *

+ */ + public class IdeaEngine + : IBlockCipher + { + private const int BLOCK_SIZE = 8; + private int[] workingKey; + /** + * standard constructor. + */ + public IdeaEngine() + { + } + /** + * initialise an IDEA cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to IDEA init - " + parameters.GetType().ToString()); + + workingKey = GenerateWorkingKey(forEncryption, + ((KeyParameter)parameters).GetKey()); + } + + public string AlgorithmName + { + get { return "IDEA"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + { + throw new InvalidOperationException("IDEA engine not initialised"); + } + if ((inOff + BLOCK_SIZE) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + if ((outOff + BLOCK_SIZE) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + IdeaFunc(workingKey, input, inOff, output, outOff); + return BLOCK_SIZE; + } + public void Reset() + { + } + private static readonly int MASK = 0xffff; + private static readonly int BASE = 0x10001; + private int BytesToWord( + byte[] input, + int inOff) + { + return ((input[inOff] << 8) & 0xff00) + (input[inOff + 1] & 0xff); + } + private void WordToBytes( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)((uint) word >> 8); + outBytes[outOff + 1] = (byte)word; + } + /** + * return x = x * y where the multiplication is done modulo + * 65537 (0x10001) (as defined in the IDEA specification) and + * a zero input is taken to be 65536 (0x10000). + * + * @param x the x value + * @param y the y value + * @return x = x * y + */ + private int Mul( + int x, + int y) + { + if (x == 0) + { + x = (BASE - y); + } + else if (y == 0) + { + x = (BASE - x); + } + else + { + int p = x * y; + y = p & MASK; + x = (int) ((uint) p >> 16); + x = y - x + ((y < x) ? 1 : 0); + } + return x & MASK; + } + private void IdeaFunc( + int[] workingKey, + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int x0, x1, x2, x3, t0, t1; + int keyOff = 0; + x0 = BytesToWord(input, inOff); + x1 = BytesToWord(input, inOff + 2); + x2 = BytesToWord(input, inOff + 4); + x3 = BytesToWord(input, inOff + 6); + for (int round = 0; round < 8; round++) + { + x0 = Mul(x0, workingKey[keyOff++]); + x1 += workingKey[keyOff++]; + x1 &= MASK; + x2 += workingKey[keyOff++]; + x2 &= MASK; + x3 = Mul(x3, workingKey[keyOff++]); + t0 = x1; + t1 = x2; + x2 ^= x0; + x1 ^= x3; + x2 = Mul(x2, workingKey[keyOff++]); + x1 += x2; + x1 &= MASK; + x1 = Mul(x1, workingKey[keyOff++]); + x2 += x1; + x2 &= MASK; + x0 ^= x1; + x3 ^= x2; + x1 ^= t1; + x2 ^= t0; + } + WordToBytes(Mul(x0, workingKey[keyOff++]), outBytes, outOff); + WordToBytes(x2 + workingKey[keyOff++], outBytes, outOff + 2); /* NB: Order */ + WordToBytes(x1 + workingKey[keyOff++], outBytes, outOff + 4); + WordToBytes(Mul(x3, workingKey[keyOff]), outBytes, outOff + 6); + } + /** + * The following function is used to expand the user key to the encryption + * subkey. The first 16 bytes are the user key, and the rest of the subkey + * is calculated by rotating the previous 16 bytes by 25 bits to the left, + * and so on until the subkey is completed. + */ + private int[] ExpandKey( + byte[] uKey) + { + int[] key = new int[52]; + if (uKey.Length < 16) + { + byte[] tmp = new byte[16]; + Array.Copy(uKey, 0, tmp, tmp.Length - uKey.Length, uKey.Length); + uKey = tmp; + } + for (int i = 0; i < 8; i++) + { + key[i] = BytesToWord(uKey, i * 2); + } + for (int i = 8; i < 52; i++) + { + if ((i & 7) < 6) + { + key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK; + } + else if ((i & 7) == 6) + { + key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK; + } + else + { + key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK; + } + } + return key; + } + /** + * This function computes multiplicative inverse using Euclid's Greatest + * Common Divisor algorithm. Zero and one are self inverse. + *

+ * i.e. x * MulInv(x) == 1 (modulo BASE) + *

+ */ + private int MulInv( + int x) + { + int t0, t1, q, y; + + if (x < 2) + { + return x; + } + t0 = 1; + t1 = BASE / x; + y = BASE % x; + while (y != 1) + { + q = x / y; + x = x % y; + t0 = (t0 + (t1 * q)) & MASK; + if (x == 1) + { + return t0; + } + q = y / x; + y = y % x; + t1 = (t1 + (t0 * q)) & MASK; + } + return (1 - t1) & MASK; + } + /** + * Return the additive inverse of x. + *

+ * i.e. x + AddInv(x) == 0 + *

+ */ + int AddInv( + int x) + { + return (0 - x) & MASK; + } + + /** + * The function to invert the encryption subkey to the decryption subkey. + * It also involves the multiplicative inverse and the additive inverse functions. + */ + private int[] InvertKey( + int[] inKey) + { + int t1, t2, t3, t4; + int p = 52; /* We work backwards */ + int[] key = new int[52]; + int inOff = 0; + + t1 = MulInv(inKey[inOff++]); + t2 = AddInv(inKey[inOff++]); + t3 = AddInv(inKey[inOff++]); + t4 = MulInv(inKey[inOff++]); + key[--p] = t4; + key[--p] = t3; + key[--p] = t2; + key[--p] = t1; + + for (int round = 1; round < 8; round++) + { + t1 = inKey[inOff++]; + t2 = inKey[inOff++]; + key[--p] = t2; + key[--p] = t1; + + t1 = MulInv(inKey[inOff++]); + t2 = AddInv(inKey[inOff++]); + t3 = AddInv(inKey[inOff++]); + t4 = MulInv(inKey[inOff++]); + key[--p] = t4; + key[--p] = t2; /* NB: Order */ + key[--p] = t3; + key[--p] = t1; + } + t1 = inKey[inOff++]; + t2 = inKey[inOff++]; + key[--p] = t2; + key[--p] = t1; + + t1 = MulInv(inKey[inOff++]); + t2 = AddInv(inKey[inOff++]); + t3 = AddInv(inKey[inOff++]); + t4 = MulInv(inKey[inOff]); + key[--p] = t4; + key[--p] = t3; + key[--p] = t2; + key[--p] = t1; + return key; + } + + private int[] GenerateWorkingKey( + bool forEncryption, + byte[] userKey) + { + if (forEncryption) + { + return ExpandKey(userKey); + } + else + { + return InvertKey(ExpandKey(userKey)); + } + } + } +} diff --git a/src/core/srcbc/crypto/engines/IesEngine.cs b/src/core/srcbc/crypto/engines/IesEngine.cs new file mode 100644 index 0000000..9282770 --- /dev/null +++ b/src/core/srcbc/crypto/engines/IesEngine.cs @@ -0,0 +1,236 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * support class for constructing intergrated encryption ciphers + * for doing basic message exchanges on top of key agreement ciphers + */ + public class IesEngine + { + private readonly IBasicAgreement agree; + private readonly IDerivationFunction kdf; + private readonly IMac mac; + private readonly BufferedBlockCipher cipher; + private readonly byte[] macBuf; + + private bool forEncryption; + private ICipherParameters privParam, pubParam; + private IesParameters param; + + /** + * set up for use with stream mode, where the key derivation function + * is used to provide a stream of bytes to xor with the message. + * + * @param agree the key agreement used as the basis for the encryption + * @param kdf the key derivation function used for byte generation + * @param mac the message authentication code generator for the message + */ + public IesEngine( + IBasicAgreement agree, + IDerivationFunction kdf, + IMac mac) + { + this.agree = agree; + this.kdf = kdf; + this.mac = mac; + this.macBuf = new byte[mac.GetMacSize()]; +// this.cipher = null; + } + + /** + * set up for use in conjunction with a block cipher to handle the + * message. + * + * @param agree the key agreement used as the basis for the encryption + * @param kdf the key derivation function used for byte generation + * @param mac the message authentication code generator for the message + * @param cipher the cipher to used for encrypting the message + */ + public IesEngine( + IBasicAgreement agree, + IDerivationFunction kdf, + IMac mac, + BufferedBlockCipher cipher) + { + this.agree = agree; + this.kdf = kdf; + this.mac = mac; + this.macBuf = new byte[mac.GetMacSize()]; + 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. + */ + public void Init( + bool forEncryption, + ICipherParameters privParameters, + ICipherParameters pubParameters, + ICipherParameters iesParameters) + { + this.forEncryption = forEncryption; + this.privParam = privParameters; + this.pubParam = pubParameters; + this.param = (IesParameters)iesParameters; + } + + private byte[] DecryptBlock( + byte[] in_enc, + int inOff, + int inLen, + byte[] z) + { + byte[] M = null; + KeyParameter macKey = null; + KdfParameters kParam = new KdfParameters(z, param.GetDerivationV()); + int macKeySize = param.MacKeySize; + + kdf.Init(kParam); + + inLen -= mac.GetMacSize(); + + if (cipher == null) // stream mode + { + byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8)); + + M = new byte[inLen]; + + for (int i = 0; i != inLen; i++) + { + M[i] = (byte)(in_enc[inOff + i] ^ Buffer[i]); + } + + macKey = new KeyParameter(Buffer, inLen, (macKeySize / 8)); + } + else + { + int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize; + byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8)); + + cipher.Init(false, new KeyParameter(Buffer, 0, (cipherKeySize / 8))); + + M = cipher.DoFinal(in_enc, inOff, inLen); + + macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8)); + } + + byte[] macIV = param.GetEncodingV(); + + mac.Init(macKey); + mac.BlockUpdate(in_enc, inOff, inLen); + mac.BlockUpdate(macIV, 0, macIV.Length); + mac.DoFinal(macBuf, 0); + + inOff += inLen; + + for (int t = 0; t < macBuf.Length; t++) + { + if (macBuf[t] != in_enc[inOff + t]) + { + throw (new InvalidCipherTextException("IMac codes failed to equal.")); + } + } + + return M; + } + + private byte[] EncryptBlock( + byte[] input, + int inOff, + int inLen, + byte[] z) + { + byte[] C = null; + KeyParameter macKey = null; + KdfParameters kParam = new KdfParameters(z, param.GetDerivationV()); + int c_text_length = 0; + int macKeySize = param.MacKeySize; + + if (cipher == null) // stream mode + { + byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8)); + + C = new byte[inLen + mac.GetMacSize()]; + c_text_length = inLen; + + for (int i = 0; i != inLen; i++) + { + C[i] = (byte)(input[inOff + i] ^ Buffer[i]); + } + + macKey = new KeyParameter(Buffer, inLen, (macKeySize / 8)); + } + else + { + int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize; + byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8)); + + cipher.Init(true, new KeyParameter(Buffer, 0, (cipherKeySize / 8))); + + c_text_length = cipher.GetOutputSize(inLen); + byte[] tmp = new byte[c_text_length]; + + int len = cipher.ProcessBytes(input, inOff, inLen, tmp, 0); + len += cipher.DoFinal(tmp, len); + + C = new byte[len + mac.GetMacSize()]; + c_text_length = len; + + Array.Copy(tmp, 0, C, 0, len); + + macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8)); + } + + byte[] macIV = param.GetEncodingV(); + + mac.Init(macKey); + mac.BlockUpdate(C, 0, c_text_length); + mac.BlockUpdate(macIV, 0, macIV.Length); + // + // return the message and it's MAC + // + mac.DoFinal(C, c_text_length); + return C; + } + + private byte[] GenerateKdfBytes( + KdfParameters kParam, + int length) + { + byte[] buf = new byte[length]; + + kdf.Init(kParam); + + kdf.GenerateBytes(buf, 0, buf.Length); + + return buf; + } + + public byte[] ProcessBlock( + byte[] input, + int inOff, + int inLen) + { + agree.Init(privParam); + + BigInteger z = agree.CalculateAgreement(pubParam); + + // TODO Check that this is right (...Unsigned? Check length?) + byte[] zBytes = z.ToByteArray(); + + return forEncryption + ? EncryptBlock(input, inOff, inLen, zBytes) + : DecryptBlock(input, inOff, inLen, zBytes); + } + } + +} diff --git a/src/core/srcbc/crypto/engines/NaccacheSternEngine.cs b/src/core/srcbc/crypto/engines/NaccacheSternEngine.cs new file mode 100644 index 0000000..94355e2 --- /dev/null +++ b/src/core/srcbc/crypto/engines/NaccacheSternEngine.cs @@ -0,0 +1,432 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * NaccacheStern Engine. For details on this cipher, please see + * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf + */ + public class NaccacheSternEngine + : IAsymmetricBlockCipher + { + private bool forEncryption; + + private NaccacheSternKeyParameters key; + + private ArrayList[] lookup = null; + + private bool debug = false; + + public string AlgorithmName + { + get { return "NaccacheStern"; } + } + + /** + * Initializes this algorithm. Must be called before all other Functions. + * + * @see org.bouncycastle.crypto.AsymmetricBlockCipher#init(bool, + * org.bouncycastle.crypto.CipherParameters) + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + key = (NaccacheSternKeyParameters)parameters; + + // construct lookup table for faster decryption if necessary + if (!this.forEncryption) + { + if (debug) + { + Console.WriteLine("Constructing lookup Array"); + } + NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; + ArrayList primes = priv.SmallPrimes; + lookup = new ArrayList[primes.Count]; + for (int i = 0; i < primes.Count; i++) + { + BigInteger actualPrime = (BigInteger) primes[i]; + int actualPrimeValue = actualPrime.IntValue; + + lookup[i] = new ArrayList(actualPrimeValue); + lookup[i].Add(BigInteger.One); + + if (debug) + { + Console.WriteLine("Constructing lookup ArrayList for " + actualPrimeValue); + } + + BigInteger accJ = BigInteger.Zero; + + for (int j = 1; j < actualPrimeValue; j++) + { +// BigInteger bigJ = BigInteger.ValueOf(j); +// accJ = priv.PhiN.Multiply(bigJ); + accJ = accJ.Add(priv.PhiN); + BigInteger comp = accJ.Divide(actualPrime); + lookup[i].Add(priv.G.ModPow(comp, priv.Modulus)); + } + } + } + } + + public bool Debug + { + set { this.debug = value; } + } + + /** + * Returns the input block size of this algorithm. + * + * @see org.bouncycastle.crypto.AsymmetricBlockCipher#GetInputBlockSize() + */ + public int GetInputBlockSize() + { + if (forEncryption) + { + // We can only encrypt values up to lowerSigmaBound + return (key.LowerSigmaBound + 7) / 8 - 1; + } + else + { + // We pad to modulus-size bytes for easier decryption. +// return key.Modulus.ToByteArray().Length; + return key.Modulus.BitLength / 8 + 1; + } + } + + /** + * Returns the output block size of this algorithm. + * + * @see org.bouncycastle.crypto.AsymmetricBlockCipher#GetOutputBlockSize() + */ + public int GetOutputBlockSize() + { + if (forEncryption) + { + // encrypted Data is always padded up to modulus size +// return key.Modulus.ToByteArray().Length; + return key.Modulus.BitLength / 8 + 1; + } + else + { + // decrypted Data has upper limit lowerSigmaBound + return (key.LowerSigmaBound + 7) / 8 - 1; + } + } + + /** + * Process a single Block using the Naccache-Stern algorithm. + * + * @see org.bouncycastle.crypto.AsymmetricBlockCipher#ProcessBlock(byte[], + * int, int) + */ + public byte[] ProcessBlock( + byte[] inBytes, + int inOff, + int length) + { + if (key == null) + throw new InvalidOperationException("NaccacheStern engine not initialised"); + if (length > (GetInputBlockSize() + 1)) + throw new DataLengthException("input too large for Naccache-Stern cipher.\n"); + + if (!forEncryption) + { + // At decryption make sure that we receive padded data blocks + if (length < GetInputBlockSize()) + { + throw new InvalidCipherTextException("BlockLength does not match modulus for Naccache-Stern cipher.\n"); + } + } + + // transform input into BigInteger + BigInteger input = new BigInteger(1, inBytes, inOff, length); + + if (debug) + { + Console.WriteLine("input as BigInteger: " + input); + } + + byte[] output; + if (forEncryption) + { + output = Encrypt(input); + } + else + { + ArrayList plain = new ArrayList(); + NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; + ArrayList primes = priv.SmallPrimes; + // Get Chinese Remainders of CipherText + for (int i = 0; i < primes.Count; i++) + { + BigInteger exp = input.ModPow(priv.PhiN.Divide((BigInteger)primes[i]), priv.Modulus); + ArrayList al = lookup[i]; + if (lookup[i].Count != ((BigInteger)primes[i]).IntValue) + { + if (debug) + { + Console.WriteLine("Prime is " + primes[i] + ", lookup table has size " + al.Count); + } + throw new InvalidCipherTextException("Error in lookup Array for " + + ((BigInteger)primes[i]).IntValue + + ": Size mismatch. Expected ArrayList with length " + + ((BigInteger)primes[i]).IntValue + " but found ArrayList of length " + + lookup[i].Count); + } + int lookedup = al.IndexOf(exp); + + if (lookedup == -1) + { + if (debug) + { + Console.WriteLine("Actual prime is " + primes[i]); + Console.WriteLine("Decrypted value is " + exp); + + Console.WriteLine("LookupList for " + primes[i] + " with size " + lookup[i].Count + + " is: "); + for (int j = 0; j < lookup[i].Count; j++) + { + Console.WriteLine(lookup[i][j]); + } + } + throw new InvalidCipherTextException("Lookup failed"); + } + plain.Add(BigInteger.ValueOf(lookedup)); + } + BigInteger test = chineseRemainder(plain, primes); + + // Should not be used as an oracle, so reencrypt output to see + // if it corresponds to input + + // this breaks probabilisic encryption, so disable it. Anyway, we do + // use the first n primes for key generation, so it is pretty easy + // to guess them. But as stated in the paper, this is not a security + // breach. So we can just work with the correct sigma. + + // if (debug) { + // Console.WriteLine("Decryption is " + test); + // } + // if ((key.G.ModPow(test, key.Modulus)).Equals(input)) { + // output = test.ToByteArray(); + // } else { + // if(debug){ + // Console.WriteLine("Engine seems to be used as an oracle, + // returning null"); + // } + // output = null; + // } + + output = test.ToByteArray(); + } + + return output; + } + + /** + * Encrypts a BigInteger aka Plaintext with the public key. + * + * @param plain + * The BigInteger to encrypt + * @return The byte[] representation of the encrypted BigInteger (i.e. + * crypted.toByteArray()) + */ + public byte[] Encrypt( + BigInteger plain) + { + // Always return modulus size values 0-padded at the beginning + // 0-padding at the beginning is correctly parsed by BigInteger :) +// byte[] output = key.Modulus.ToByteArray(); +// Array.Clear(output, 0, output.Length); + byte[] output = new byte[key.Modulus.BitLength / 8 + 1]; + + byte[] tmp = key.G.ModPow(plain, key.Modulus).ToByteArray(); + Array.Copy(tmp, 0, output, output.Length - tmp.Length, tmp.Length); + if (debug) + { + Console.WriteLine("Encrypted value is: " + new BigInteger(output)); + } + return output; + } + + /** + * Adds the contents of two encrypted blocks mod sigma + * + * @param block1 + * the first encrypted block + * @param block2 + * the second encrypted block + * @return encrypt((block1 + block2) mod sigma) + * @throws InvalidCipherTextException + */ + public byte[] AddCryptedBlocks( + byte[] block1, + byte[] block2) + { + // check for correct blocksize + if (forEncryption) + { + if ((block1.Length > GetOutputBlockSize()) + || (block2.Length > GetOutputBlockSize())) + { + throw new InvalidCipherTextException( + "BlockLength too large for simple addition.\n"); + } + } + else + { + if ((block1.Length > GetInputBlockSize()) + || (block2.Length > GetInputBlockSize())) + { + throw new InvalidCipherTextException( + "BlockLength too large for simple addition.\n"); + } + } + + // calculate resulting block + BigInteger m1Crypt = new BigInteger(1, block1); + BigInteger m2Crypt = new BigInteger(1, block2); + BigInteger m1m2Crypt = m1Crypt.Multiply(m2Crypt); + m1m2Crypt = m1m2Crypt.Mod(key.Modulus); + if (debug) + { + Console.WriteLine("c(m1) as BigInteger:....... " + m1Crypt); + Console.WriteLine("c(m2) as BigInteger:....... " + m2Crypt); + Console.WriteLine("c(m1)*c(m2)%n = c(m1+m2)%n: " + m1m2Crypt); + } + + //byte[] output = key.Modulus.ToByteArray(); + //Array.Clear(output, 0, output.Length); + byte[] output = new byte[key.Modulus.BitLength / 8 + 1]; + + byte[] m1m2CryptBytes = m1m2Crypt.ToByteArray(); + Array.Copy(m1m2CryptBytes, 0, output, + output.Length - m1m2CryptBytes.Length, m1m2CryptBytes.Length); + + return output; + } + + /** + * Convenience Method for data exchange with the cipher. + * + * Determines blocksize and splits data to blocksize. + * + * @param data the data to be processed + * @return the data after it went through the NaccacheSternEngine. + * @throws InvalidCipherTextException + */ + public byte[] ProcessData( + byte[] data) + { + if (debug) + { + Console.WriteLine(); + } + if (data.Length > GetInputBlockSize()) + { + int inBlocksize = GetInputBlockSize(); + int outBlocksize = GetOutputBlockSize(); + if (debug) + { + Console.WriteLine("Input blocksize is: " + inBlocksize + " bytes"); + Console.WriteLine("Output blocksize is: " + outBlocksize + " bytes"); + Console.WriteLine("Data has length:.... " + data.Length + " bytes"); + } + int datapos = 0; + int retpos = 0; + byte[] retval = new byte[(data.Length / inBlocksize + 1) * outBlocksize]; + while (datapos < data.Length) + { + byte[] tmp; + if (datapos + inBlocksize < data.Length) + { + tmp = ProcessBlock(data, datapos, inBlocksize); + datapos += inBlocksize; + } + else + { + tmp = ProcessBlock(data, datapos, data.Length - datapos); + datapos += data.Length - datapos; + } + if (debug) + { + Console.WriteLine("new datapos is " + datapos); + } + if (tmp != null) + { + tmp.CopyTo(retval, retpos); + retpos += tmp.Length; + } + else + { + if (debug) + { + Console.WriteLine("cipher returned null"); + } + throw new InvalidCipherTextException("cipher returned null"); + } + } + byte[] ret = new byte[retpos]; + Array.Copy(retval, 0, ret, 0, retpos); + if (debug) + { + Console.WriteLine("returning " + ret.Length + " bytes"); + } + return ret; + } + else + { + if (debug) + { + Console.WriteLine("data size is less then input block size, processing directly"); + } + return ProcessBlock(data, 0, data.Length); + } + } + + /** + * Computes the integer x that is expressed through the given primes and the + * congruences with the chinese remainder theorem (CRT). + * + * @param congruences + * the congruences c_i + * @param primes + * the primes p_i + * @return an integer x for that x % p_i == c_i + */ + private static BigInteger chineseRemainder(ArrayList congruences, ArrayList primes) + { + BigInteger retval = BigInteger.Zero; + BigInteger all = BigInteger.One; + for (int i = 0; i < primes.Count; i++) + { + all = all.Multiply((BigInteger)primes[i]); + } + for (int i = 0; i < primes.Count; i++) + { + BigInteger a = (BigInteger)primes[i]; + BigInteger b = all.Divide(a); + BigInteger b_ = b.ModInverse(a); + BigInteger tmp = b.Multiply(b_); + tmp = tmp.Multiply((BigInteger)congruences[i]); + retval = retval.Add(tmp); + } + + return retval.Mod(all); + } + } +} diff --git a/src/core/srcbc/crypto/engines/NoekeonEngine.cs b/src/core/srcbc/crypto/engines/NoekeonEngine.cs new file mode 100644 index 0000000..618007f --- /dev/null +++ b/src/core/srcbc/crypto/engines/NoekeonEngine.cs @@ -0,0 +1,256 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * A Noekeon engine, using direct-key mode. + */ + public class NoekeonEngine + : IBlockCipher + { + private const int GenericSize = 16; // Block and key size, as well as the amount of rounds. + + private static readonly uint[] nullVector = + { + 0x00, 0x00, 0x00, 0x00 // Used in decryption + }; + + private static readonly uint[] roundConstants = + { + 0x80, 0x1b, 0x36, 0x6c, + 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, + 0xc6, 0x97, 0x35, 0x6a, + 0xd4 + }; + + private uint[] state = new uint[4], // a + subKeys = new uint[4], // k + decryptKeys = new uint[4]; + + private bool _initialised, _forEncryption; + + /** + * Create an instance of the Noekeon encryption algorithm + * and set some defaults + */ + public NoekeonEngine() + { + _initialised = false; + } + + public string AlgorithmName + { + get { return "Noekeon"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return GenericSize; + } + + /** + * initialise + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("Invalid parameters passed to Noekeon init - " + parameters.GetType().Name, "parameters"); + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter) parameters; + + setKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + GenericSize) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + GenericSize) > output.Length) + throw new DataLengthException("output buffer too short"); + + return _forEncryption + ? encryptBlock(input, inOff, output, outOff) + : decryptBlock(input, inOff, output, outOff); + } + + public void Reset() + { + // TODO This should do something in case the encryption is aborted + } + + /** + * Re-key the cipher. + * + * @param key the key to be used + */ + private void setKey(byte[] key) + { + subKeys[0] = bytesToIntBig(key, 0); + subKeys[1] = bytesToIntBig(key, 4); + subKeys[2] = bytesToIntBig(key, 8); + subKeys[3] = bytesToIntBig(key, 12); + } + + private int encryptBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + state[0] = bytesToIntBig(input, inOff); + state[1] = bytesToIntBig(input, inOff+4); + state[2] = bytesToIntBig(input, inOff+8); + state[3] = bytesToIntBig(input, inOff+12); + + int i; + for (i = 0; i < GenericSize; i++) + { + state[0] ^= roundConstants[i]; + theta(state, subKeys); + pi1(state); + gamma(state); + pi2(state); + } + + state[0] ^= roundConstants[i]; + theta(state, subKeys); + + intToBytesBig(state[0], output, outOff); + intToBytesBig(state[1], output, outOff+4); + intToBytesBig(state[2], output, outOff+8); + intToBytesBig(state[3], output, outOff+12); + + return GenericSize; + } + + private int decryptBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + state[0] = bytesToIntBig(input, inOff); + state[1] = bytesToIntBig(input, inOff+4); + state[2] = bytesToIntBig(input, inOff+8); + state[3] = bytesToIntBig(input, inOff+12); + + Array.Copy(subKeys, 0, decryptKeys, 0, subKeys.Length); + theta(decryptKeys, nullVector); + + int i; + for (i = GenericSize; i > 0; i--) + { + theta(state, decryptKeys); + state[0] ^= roundConstants[i]; + pi1(state); + gamma(state); + pi2(state); + } + + theta(state, decryptKeys); + state[0] ^= roundConstants[i]; + + intToBytesBig(state[0], output, outOff); + intToBytesBig(state[1], output, outOff+4); + intToBytesBig(state[2], output, outOff+8); + intToBytesBig(state[3], output, outOff+12); + + return GenericSize; + } + + private void gamma(uint[] a) + { + a[1] ^= ~a[3] & ~a[2]; + a[0] ^= a[2] & a[1]; + + uint tmp = a[3]; + a[3] = a[0]; + a[0] = tmp; + a[2] ^= a[0]^a[1]^a[3]; + + a[1] ^= ~a[3] & ~a[2]; + a[0] ^= a[2] & a[1]; + } + + private void theta(uint[] a, uint[] k) + { + uint tmp; + tmp = a[0]^a[2]; + tmp ^= rotl(tmp,8)^rotl(tmp,24); + a[1] ^= tmp; + a[3] ^= tmp; + + for (int i = 0; i < 4; i++) + { + a[i] ^= k[i]; + } + + tmp = a[1]^a[3]; + tmp ^= rotl(tmp,8)^rotl(tmp,24); + a[0] ^= tmp; + a[2] ^= tmp; + } + + private void pi1(uint[] a) + { + a[1] = rotl(a[1], 1); + a[2] = rotl(a[2], 5); + a[3] = rotl(a[3], 2); + } + + private void pi2(uint[] a) + { + a[1] = rotl(a[1], 31); + a[2] = rotl(a[2], 27); + a[3] = rotl(a[3], 30); + } + + // Helpers + + private uint bytesToIntBig(byte[] input, int off) + { + int result = ((input[off++]) << 24) | + ((input[off++] & 0xff) << 16) | + ((input[off++] & 0xff) << 8) | + (input[off ] & 0xff); + return (uint) result; + } + + private void intToBytesBig(uint x, byte[] output, int off) + { + output[off++] = (byte)(x >> 24); + output[off++] = (byte)(x >> 16); + output[off++] = (byte)(x >> 8); + output[off ] = (byte)x; + } + + private uint rotl(uint x, int y) + { + return (x << y) | (x >> (32-y)); + } + } +} diff --git a/src/core/srcbc/crypto/engines/NullEngine.cs b/src/core/srcbc/crypto/engines/NullEngine.cs new file mode 100644 index 0000000..22a4e4c --- /dev/null +++ b/src/core/srcbc/crypto/engines/NullEngine.cs @@ -0,0 +1,70 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * The no-op engine that just copies bytes through, irrespective of whether encrypting and decrypting. + * Provided for the sake of completeness. + */ + public class NullEngine + : IBlockCipher + { + private bool initialised; + private const int BlockSize = 1; + + public NullEngine() + { + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + // we don't mind any parameters that may come in + initialised = true; + } + + public string AlgorithmName + { + get { return "Null"; } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + public int GetBlockSize() + { + return BlockSize; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException("Null engine not initialised"); + if ((inOff + BlockSize) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BlockSize) > output.Length) + throw new DataLengthException("output buffer too short"); + + for (int i = 0; i < BlockSize; ++i) + { + output[outOff + i] = input[inOff + i]; + } + + return BlockSize; + } + + public void Reset() + { + // nothing needs to be done + } + } +} diff --git a/src/core/srcbc/crypto/engines/RC2Engine.cs b/src/core/srcbc/crypto/engines/RC2Engine.cs new file mode 100644 index 0000000..3f3a513 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RC2Engine.cs @@ -0,0 +1,312 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of RC2 as described in RFC 2268 + * "A Description of the RC2(r) Encryption Algorithm" R. Rivest. + */ + public class RC2Engine + : IBlockCipher + { + // + // the values we use for key expansion (based on the digits of PI) + // + private static readonly byte[] piTable = + { + (byte)0xd9, (byte)0x78, (byte)0xf9, (byte)0xc4, (byte)0x19, (byte)0xdd, (byte)0xb5, (byte)0xed, + (byte)0x28, (byte)0xe9, (byte)0xfd, (byte)0x79, (byte)0x4a, (byte)0xa0, (byte)0xd8, (byte)0x9d, + (byte)0xc6, (byte)0x7e, (byte)0x37, (byte)0x83, (byte)0x2b, (byte)0x76, (byte)0x53, (byte)0x8e, + (byte)0x62, (byte)0x4c, (byte)0x64, (byte)0x88, (byte)0x44, (byte)0x8b, (byte)0xfb, (byte)0xa2, + (byte)0x17, (byte)0x9a, (byte)0x59, (byte)0xf5, (byte)0x87, (byte)0xb3, (byte)0x4f, (byte)0x13, + (byte)0x61, (byte)0x45, (byte)0x6d, (byte)0x8d, (byte)0x9, (byte)0x81, (byte)0x7d, (byte)0x32, + (byte)0xbd, (byte)0x8f, (byte)0x40, (byte)0xeb, (byte)0x86, (byte)0xb7, (byte)0x7b, (byte)0xb, + (byte)0xf0, (byte)0x95, (byte)0x21, (byte)0x22, (byte)0x5c, (byte)0x6b, (byte)0x4e, (byte)0x82, + (byte)0x54, (byte)0xd6, (byte)0x65, (byte)0x93, (byte)0xce, (byte)0x60, (byte)0xb2, (byte)0x1c, + (byte)0x73, (byte)0x56, (byte)0xc0, (byte)0x14, (byte)0xa7, (byte)0x8c, (byte)0xf1, (byte)0xdc, + (byte)0x12, (byte)0x75, (byte)0xca, (byte)0x1f, (byte)0x3b, (byte)0xbe, (byte)0xe4, (byte)0xd1, + (byte)0x42, (byte)0x3d, (byte)0xd4, (byte)0x30, (byte)0xa3, (byte)0x3c, (byte)0xb6, (byte)0x26, + (byte)0x6f, (byte)0xbf, (byte)0xe, (byte)0xda, (byte)0x46, (byte)0x69, (byte)0x7, (byte)0x57, + (byte)0x27, (byte)0xf2, (byte)0x1d, (byte)0x9b, (byte)0xbc, (byte)0x94, (byte)0x43, (byte)0x3, + (byte)0xf8, (byte)0x11, (byte)0xc7, (byte)0xf6, (byte)0x90, (byte)0xef, (byte)0x3e, (byte)0xe7, + (byte)0x6, (byte)0xc3, (byte)0xd5, (byte)0x2f, (byte)0xc8, (byte)0x66, (byte)0x1e, (byte)0xd7, + (byte)0x8, (byte)0xe8, (byte)0xea, (byte)0xde, (byte)0x80, (byte)0x52, (byte)0xee, (byte)0xf7, + (byte)0x84, (byte)0xaa, (byte)0x72, (byte)0xac, (byte)0x35, (byte)0x4d, (byte)0x6a, (byte)0x2a, + (byte)0x96, (byte)0x1a, (byte)0xd2, (byte)0x71, (byte)0x5a, (byte)0x15, (byte)0x49, (byte)0x74, + (byte)0x4b, (byte)0x9f, (byte)0xd0, (byte)0x5e, (byte)0x4, (byte)0x18, (byte)0xa4, (byte)0xec, + (byte)0xc2, (byte)0xe0, (byte)0x41, (byte)0x6e, (byte)0xf, (byte)0x51, (byte)0xcb, (byte)0xcc, + (byte)0x24, (byte)0x91, (byte)0xaf, (byte)0x50, (byte)0xa1, (byte)0xf4, (byte)0x70, (byte)0x39, + (byte)0x99, (byte)0x7c, (byte)0x3a, (byte)0x85, (byte)0x23, (byte)0xb8, (byte)0xb4, (byte)0x7a, + (byte)0xfc, (byte)0x2, (byte)0x36, (byte)0x5b, (byte)0x25, (byte)0x55, (byte)0x97, (byte)0x31, + (byte)0x2d, (byte)0x5d, (byte)0xfa, (byte)0x98, (byte)0xe3, (byte)0x8a, (byte)0x92, (byte)0xae, + (byte)0x5, (byte)0xdf, (byte)0x29, (byte)0x10, (byte)0x67, (byte)0x6c, (byte)0xba, (byte)0xc9, + (byte)0xd3, (byte)0x0, (byte)0xe6, (byte)0xcf, (byte)0xe1, (byte)0x9e, (byte)0xa8, (byte)0x2c, + (byte)0x63, (byte)0x16, (byte)0x1, (byte)0x3f, (byte)0x58, (byte)0xe2, (byte)0x89, (byte)0xa9, + (byte)0xd, (byte)0x38, (byte)0x34, (byte)0x1b, (byte)0xab, (byte)0x33, (byte)0xff, (byte)0xb0, + (byte)0xbb, (byte)0x48, (byte)0xc, (byte)0x5f, (byte)0xb9, (byte)0xb1, (byte)0xcd, (byte)0x2e, + (byte)0xc5, (byte)0xf3, (byte)0xdb, (byte)0x47, (byte)0xe5, (byte)0xa5, (byte)0x9c, (byte)0x77, + (byte)0xa, (byte)0xa6, (byte)0x20, (byte)0x68, (byte)0xfe, (byte)0x7f, (byte)0xc1, (byte)0xad + }; + + private const int BLOCK_SIZE = 8; + + private int[] workingKey; + private bool encrypting; + + private int[] GenerateWorkingKey( + byte[] key, + int bits) + { + int x; + int[] xKey = new int[128]; + + for (int i = 0; i != key.Length; i++) + { + xKey[i] = key[i] & 0xff; + } + + // Phase 1: Expand input key to 128 bytes + int len = key.Length; + + if (len < 128) + { + int index = 0; + + x = xKey[len - 1]; + + do + { + x = piTable[(x + xKey[index++]) & 255] & 0xff; + xKey[len++] = x; + } + while (len < 128); + } + + // Phase 2 - reduce effective key size to "bits" + len = (bits + 7) >> 3; + x = piTable[xKey[128 - len] & (255 >> (7 & -bits))] & 0xff; + xKey[128 - len] = x; + + for (int i = 128 - len - 1; i >= 0; i--) + { + x = piTable[x ^ xKey[i + len]] & 0xff; + xKey[i] = x; + } + + // Phase 3 - copy to newKey in little-endian order + int[] newKey = new int[64]; + + for (int i = 0; i != newKey.Length; i++) + { + newKey[i] = (xKey[2 * i] + (xKey[2 * i + 1] << 8)); + } + + return newKey; + } + + /** + * initialise a RC2 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.encrypting = forEncryption; + + if (parameters is RC2Parameters) + { + RC2Parameters param = (RC2Parameters) parameters; + + workingKey = GenerateWorkingKey(param.GetKey(), param.EffectiveKeyBits); + } + else if (parameters is KeyParameter) + { + KeyParameter param = (KeyParameter) parameters; + byte[] key = param.GetKey(); + + workingKey = GenerateWorkingKey(key, key.Length * 8); + } + else + { + throw new ArgumentException("invalid parameter passed to RC2 init - " + parameters.GetType().Name); + } + } + + public void Reset() + { + } + + public string AlgorithmName + { + get { return "RC2"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + throw new InvalidOperationException("RC2 engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, outOff); + } + + return BLOCK_SIZE; + } + + /** + * return the result rotating the 16 bit number in x left by y + */ + private int RotateWordLeft( + int x, + int y) + { + x &= 0xffff; + return (x << y) | (x >> (16 - y)); + } + + private void EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int x76, x54, x32, x10; + + x76 = ((input[inOff + 7] & 0xff) << 8) + (input[inOff + 6] & 0xff); + x54 = ((input[inOff + 5] & 0xff) << 8) + (input[inOff + 4] & 0xff); + x32 = ((input[inOff + 3] & 0xff) << 8) + (input[inOff + 2] & 0xff); + x10 = ((input[inOff + 1] & 0xff) << 8) + (input[inOff + 0] & 0xff); + + for (int i = 0; i <= 16; i += 4) + { + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); + } + + x10 += workingKey[x76 & 63]; + x32 += workingKey[x10 & 63]; + x54 += workingKey[x32 & 63]; + x76 += workingKey[x54 & 63]; + + for (int i = 20; i <= 40; i += 4) + { + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); + } + + x10 += workingKey[x76 & 63]; + x32 += workingKey[x10 & 63]; + x54 += workingKey[x32 & 63]; + x76 += workingKey[x54 & 63]; + + for (int i = 44; i < 64; i += 4) + { + x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i ], 1); + x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2); + x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3); + x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5); + } + + outBytes[outOff + 0] = (byte)x10; + outBytes[outOff + 1] = (byte)(x10 >> 8); + outBytes[outOff + 2] = (byte)x32; + outBytes[outOff + 3] = (byte)(x32 >> 8); + outBytes[outOff + 4] = (byte)x54; + outBytes[outOff + 5] = (byte)(x54 >> 8); + outBytes[outOff + 6] = (byte)x76; + outBytes[outOff + 7] = (byte)(x76 >> 8); + } + + private void DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int x76, x54, x32, x10; + + x76 = ((input[inOff + 7] & 0xff) << 8) + (input[inOff + 6] & 0xff); + x54 = ((input[inOff + 5] & 0xff) << 8) + (input[inOff + 4] & 0xff); + x32 = ((input[inOff + 3] & 0xff) << 8) + (input[inOff + 2] & 0xff); + x10 = ((input[inOff + 1] & 0xff) << 8) + (input[inOff + 0] & 0xff); + + for (int i = 60; i >= 44; i -= 4) + { + x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]); + x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]); + x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]); + x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]); + } + + x76 -= workingKey[x54 & 63]; + x54 -= workingKey[x32 & 63]; + x32 -= workingKey[x10 & 63]; + x10 -= workingKey[x76 & 63]; + + for (int i = 40; i >= 20; i -= 4) + { + x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]); + x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]); + x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]); + x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]); + } + + x76 -= workingKey[x54 & 63]; + x54 -= workingKey[x32 & 63]; + x32 -= workingKey[x10 & 63]; + x10 -= workingKey[x76 & 63]; + + for (int i = 16; i >= 0; i -= 4) + { + x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i+3]); + x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i+2]); + x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i+1]); + x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i ]); + } + + outBytes[outOff + 0] = (byte)x10; + outBytes[outOff + 1] = (byte)(x10 >> 8); + outBytes[outOff + 2] = (byte)x32; + outBytes[outOff + 3] = (byte)(x32 >> 8); + outBytes[outOff + 4] = (byte)x54; + outBytes[outOff + 5] = (byte)(x54 >> 8); + outBytes[outOff + 6] = (byte)x76; + outBytes[outOff + 7] = (byte)(x76 >> 8); + } + } + +} diff --git a/src/core/srcbc/crypto/engines/RC2WrapEngine.cs b/src/core/srcbc/crypto/engines/RC2WrapEngine.cs new file mode 100644 index 0000000..722f91f --- /dev/null +++ b/src/core/srcbc/crypto/engines/RC2WrapEngine.cs @@ -0,0 +1,372 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Wrap keys according to RFC 3217 - RC2 mechanism + */ + public class RC2WrapEngine + : IWrapper + { + /** Field engine */ + private CbcBlockCipher engine; + + /** Field param */ + private ICipherParameters parameters; + + /** Field paramPlusIV */ + private ParametersWithIV paramPlusIV; + + /** Field iv */ + private byte[] iv; + + /** Field forWrapping */ + private bool forWrapping; + + private SecureRandom sr; + + /** Field IV2 */ + private static readonly byte[] IV2 = + { + (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, + (byte) 0x2c, (byte) 0x79, (byte) 0xe8, + (byte) 0x21, (byte) 0x05 + }; + + // + // checksum digest + // + IDigest sha1 = new Sha1Digest(); + byte[] digest = new byte[20]; + + /** + * Method init + * + * @param forWrapping + * @param param + */ + public void Init( + bool forWrapping, + ICipherParameters parameters) + { + this.forWrapping = forWrapping; + this.engine = new CbcBlockCipher(new RC2Engine()); + + if (parameters is ParametersWithRandom) + { + ParametersWithRandom pWithR = (ParametersWithRandom)parameters; + sr = pWithR.Random; + parameters = pWithR.Parameters; + } + else + { + sr = new SecureRandom(); + } + + if (parameters is ParametersWithIV) + { + if (!forWrapping) + throw new ArgumentException("You should not supply an IV for unwrapping"); + + this.paramPlusIV = (ParametersWithIV)parameters; + this.iv = this.paramPlusIV.GetIV(); + this.parameters = this.paramPlusIV.Parameters; + + if (this.iv.Length != 8) + throw new ArgumentException("IV is not 8 octets"); + } + else + { + this.parameters = parameters; + + if (this.forWrapping) + { + // Hm, we have no IV but we want to wrap ?!? + // well, then we have to create our own IV. + this.iv = new byte[8]; + sr.NextBytes(iv); + this.paramPlusIV = new ParametersWithIV(this.parameters, this.iv); + } + } + } + + /** + * Method GetAlgorithmName + * + * @return + */ + public string AlgorithmName + { + get { return "RC2"; } + } + + /** + * Method wrap + * + * @param in + * @param inOff + * @param inLen + * @return + */ + public byte[] Wrap( + byte[] input, + int inOff, + int length) + { + if (!forWrapping) + { + throw new InvalidOperationException("Not initialized for wrapping"); + } + + int len = length + 1; + if ((len % 8) != 0) + { + len += 8 - (len % 8); + } + + byte [] keyToBeWrapped = new byte[len]; + + keyToBeWrapped[0] = (byte)length; + Array.Copy(input, inOff, keyToBeWrapped, 1, length); + + byte[] pad = new byte[keyToBeWrapped.Length - length - 1]; + + if (pad.Length > 0) + { + sr.NextBytes(pad); + Array.Copy(pad, 0, keyToBeWrapped, length + 1, pad.Length); + } + + // Compute the CMS Key Checksum, (section 5.6.1), call this CKS. + byte[] CKS = CalculateCmsKeyChecksum(keyToBeWrapped); + + // Let WKCKS = WK || CKS where || is concatenation. + byte[] WKCKS = new byte[keyToBeWrapped.Length + CKS.Length]; + + Array.Copy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.Length); + Array.Copy(CKS, 0, WKCKS, keyToBeWrapped.Length, CKS.Length); + + // Encrypt WKCKS in CBC mode using KEK as the key and IV as the + // initialization vector. Call the results TEMP1. + byte [] TEMP1 = new byte[WKCKS.Length]; + + Array.Copy(WKCKS, 0, TEMP1, 0, WKCKS.Length); + + int noOfBlocks = WKCKS.Length / engine.GetBlockSize(); + int extraBytes = WKCKS.Length % engine.GetBlockSize(); + + if (extraBytes != 0) + { + throw new InvalidOperationException("Not multiple of block length"); + } + + engine.Init(true, paramPlusIV); + + for (int i = 0; i < noOfBlocks; i++) + { + int currentBytePos = i * engine.GetBlockSize(); + + engine.ProcessBlock(TEMP1, currentBytePos, TEMP1, currentBytePos); + } + + // Left TEMP2 = IV || TEMP1. + byte[] TEMP2 = new byte[this.iv.Length + TEMP1.Length]; + + Array.Copy(this.iv, 0, TEMP2, 0, this.iv.Length); + Array.Copy(TEMP1, 0, TEMP2, this.iv.Length, TEMP1.Length); + + // Reverse the order of the octets in TEMP2 and call the result TEMP3. + byte[] TEMP3 = new byte[TEMP2.Length]; + + for (int i = 0; i < TEMP2.Length; i++) + { + TEMP3[i] = TEMP2[TEMP2.Length - (i + 1)]; + } + + // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector + // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired + // result. It is 40 octets long if a 168 bit key is being wrapped. + ParametersWithIV param2 = new ParametersWithIV(this.parameters, IV2); + + this.engine.Init(true, param2); + + for (int i = 0; i < noOfBlocks + 1; i++) + { + int currentBytePos = i * engine.GetBlockSize(); + + engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos); + } + + return TEMP3; + } + + /** + * Method unwrap + * + * @param in + * @param inOff + * @param inLen + * @return + * @throws InvalidCipherTextException + */ + public byte[] Unwrap( + byte[] input, + int inOff, + int length) + { + if (forWrapping) + { + throw new InvalidOperationException("Not set for unwrapping"); + } + + if (input == null) + { + throw new InvalidCipherTextException("Null pointer as ciphertext"); + } + + if (length % engine.GetBlockSize() != 0) + { + throw new InvalidCipherTextException("Ciphertext not multiple of " + + engine.GetBlockSize()); + } + + /* + // Check if the length of the cipher text is reasonable given the key + // type. It must be 40 bytes for a 168 bit key and either 32, 40, or + // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported + // or inconsistent with the algorithm for which the key is intended, + // return error. + // + // we do not accept 168 bit keys. it has to be 192 bit. + int lengthA = (estimatedKeyLengthInBit / 8) + 16; + int lengthB = estimatedKeyLengthInBit % 8; + + if ((lengthA != keyToBeUnwrapped.Length) || (lengthB != 0)) { + throw new XMLSecurityException("empty"); + } + */ + + // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK + // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3. + ParametersWithIV param2 = new ParametersWithIV(this.parameters, IV2); + + this.engine.Init(false, param2); + + byte [] TEMP3 = new byte[length]; + + Array.Copy(input, inOff, TEMP3, 0, length); + + for (int i = 0; i < (TEMP3.Length / engine.GetBlockSize()); i++) + { + int currentBytePos = i * engine.GetBlockSize(); + + engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos); + } + + // Reverse the order of the octets in TEMP3 and call the result TEMP2. + byte[] TEMP2 = new byte[TEMP3.Length]; + + for (int i = 0; i < TEMP3.Length; i++) + { + TEMP2[i] = TEMP3[TEMP3.Length - (i + 1)]; + } + + // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets. + this.iv = new byte[8]; + + byte[] TEMP1 = new byte[TEMP2.Length - 8]; + + Array.Copy(TEMP2, 0, this.iv, 0, 8); + Array.Copy(TEMP2, 8, TEMP1, 0, TEMP2.Length - 8); + + // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV + // found in the previous step. Call the result WKCKS. + this.paramPlusIV = new ParametersWithIV(this.parameters, this.iv); + + this.engine.Init(false, this.paramPlusIV); + + byte[] LCEKPADICV = new byte[TEMP1.Length]; + + Array.Copy(TEMP1, 0, LCEKPADICV, 0, TEMP1.Length); + + for (int i = 0; i < (LCEKPADICV.Length / engine.GetBlockSize()); i++) + { + int currentBytePos = i * engine.GetBlockSize(); + + engine.ProcessBlock(LCEKPADICV, currentBytePos, LCEKPADICV, currentBytePos); + } + + // Decompose LCEKPADICV. CKS is the last 8 octets and WK, the wrapped key, are + // those octets before the CKS. + byte[] result = new byte[LCEKPADICV.Length - 8]; + byte[] CKStoBeVerified = new byte[8]; + + Array.Copy(LCEKPADICV, 0, result, 0, LCEKPADICV.Length - 8); + Array.Copy(LCEKPADICV, LCEKPADICV.Length - 8, CKStoBeVerified, 0, 8); + + // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare + // with the CKS extracted in the above step. If they are not equal, return error. + if (!CheckCmsKeyChecksum(result, CKStoBeVerified)) + { + throw new InvalidCipherTextException( + "Checksum inside ciphertext is corrupted"); + } + + if ((result.Length - ((result[0] & 0xff) + 1)) > 7) + { + throw new InvalidCipherTextException( + "too many pad bytes (" + (result.Length - ((result[0] & 0xff) + 1)) + ")"); + } + + // CEK is the wrapped key, now extracted for use in data decryption. + byte[] CEK = new byte[result[0]]; + Array.Copy(result, 1, CEK, 0, CEK.Length); + return CEK; + } + + /** + * Some key wrap algorithms make use of the Key Checksum defined + * in CMS [CMS-Algorithms]. This is used to provide an integrity + * check value for the key being wrapped. The algorithm is + * + * - 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 + * @return + * @throws Exception + * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum + */ + private byte[] CalculateCmsKeyChecksum( + byte[] key) + { + byte[] result = new byte[8]; + + sha1.BlockUpdate(key, 0, key.Length); + sha1.DoFinal(digest, 0); + + Array.Copy(digest, 0, result, 0, 8); + + return result; + } + + /** + * @param key + * @param checksum + * @return + * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum + */ + private bool CheckCmsKeyChecksum( + byte[] key, + byte[] checksum) + { + return Arrays.AreEqual(CalculateCmsKeyChecksum(key), checksum); + } + } +} diff --git a/src/core/srcbc/crypto/engines/RC4Engine.cs b/src/core/srcbc/crypto/engines/RC4Engine.cs new file mode 100644 index 0000000..76bae34 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RC4Engine.cs @@ -0,0 +1,147 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + public class RC4Engine + : IStreamCipher + { + private readonly static int STATE_LENGTH = 256; + + /* + * variables to hold the state of the RC4 engine + * during encryption and decryption + */ + + private byte[] engineState; + private int x; + private int y; + private byte[] workingKey; + + /** + * initialise a RC4 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is KeyParameter) + { + /* + * RC4 encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. + */ + workingKey = ((KeyParameter)parameters).GetKey(); + SetKey(workingKey); + + return; + } + + throw new ArgumentException("invalid parameter passed to RC4 init - " + parameters.GetType().ToString()); + } + + public string AlgorithmName + { + get { return "RC4"; } + } + + public byte ReturnByte( + byte input) + { + x = (x + 1) & 0xff; + y = (engineState[x] + y) & 0xff; + + // swap + byte tmp = engineState[x]; + engineState[x] = engineState[y]; + engineState[y] = tmp; + + // xor + return (byte)(input ^ engineState[(engineState[x] + engineState[y]) & 0xff]); + } + + public void ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff + ) + { + if ((inOff + length) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + length) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + for (int i = 0; i < length ; i++) + { + x = (x + 1) & 0xff; + y = (engineState[x] + y) & 0xff; + + // swap + byte tmp = engineState[x]; + engineState[x] = engineState[y]; + engineState[y] = tmp; + + // xor + output[i+outOff] = (byte)(input[i + inOff] + ^ engineState[(engineState[x] + engineState[y]) & 0xff]); + } + } + + public void Reset() + { + SetKey(workingKey); + } + + // Private implementation + + private void SetKey( + byte[] keyBytes) + { + workingKey = keyBytes; + + // System.out.println("the key length is ; "+ workingKey.Length); + + x = 0; + y = 0; + + if (engineState == null) + { + engineState = new byte[STATE_LENGTH]; + } + + // reset the state of the engine + for (int i=0; i < STATE_LENGTH; i++) + { + engineState[i] = (byte)i; + } + + int i1 = 0; + int i2 = 0; + + for (int i=0; i < STATE_LENGTH; i++) + { + i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff; + // do the byte-swap inline + byte tmp = engineState[i]; + engineState[i] = engineState[i2]; + engineState[i2] = tmp; + i1 = (i1+1) % keyBytes.Length; + } + } + } + +} diff --git a/src/core/srcbc/crypto/engines/RC532Engine.cs b/src/core/srcbc/crypto/engines/RC532Engine.cs new file mode 100644 index 0000000..3f34031 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RC532Engine.cs @@ -0,0 +1,294 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * The specification for RC5 came from the RC5 Encryption Algorithm + * publication in RSA CryptoBytes, Spring of 1995. + * http://www.rsasecurity.com/rsalabs/cryptobytes. + *

+ * This implementation has a word size of 32 bits.

+ */ + public class RC532Engine + : IBlockCipher + { + /* + * the number of rounds to perform + */ + private int _noRounds; + + /* + * the expanded key array of size 2*(rounds + 1) + */ + private int [] _S; + + /* + * our "magic constants" for 32 32 + * + * Pw = Odd((e-2) * 2^wordsize) + * Qw = Odd((o-2) * 2^wordsize) + * + * where e is the base of natural logarithms (2.718281828...) + * and o is the golden ratio (1.61803398...) + */ + private static readonly int P32 = unchecked((int) 0xb7e15163); + private static readonly int Q32 = unchecked((int) 0x9e3779b9); + + private bool forEncryption; + + /** + * Create an instance of the RC5 encryption algorithm + * and set some defaults + */ + public RC532Engine() + { + _noRounds = 12; // the default +// _S = null; + } + + public string AlgorithmName + { + get { return "RC5-32"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return 2 * 4; + } + + /** + * initialise a RC5-32 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (typeof(RC5Parameters).IsInstanceOfType(parameters)) + { + RC5Parameters p = (RC5Parameters)parameters; + + _noRounds = p.Rounds; + + SetKey(p.GetKey()); + } + else if (typeof(KeyParameter).IsInstanceOfType(parameters)) + { + KeyParameter p = (KeyParameter)parameters; + + SetKey(p.GetKey()); + } + else + { + throw new ArgumentException("invalid parameter passed to RC532 init - " + parameters.GetType().ToString()); + } + + this.forEncryption = forEncryption; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (forEncryption) + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @param key the key to be used + */ + private void SetKey( + byte[] key) + { + // + // KEY EXPANSION: + // + // There are 3 phases to the key expansion. + // + // Phase 1: + // Copy the secret key K[0...b-1] into an array L[0..c-1] of + // c = ceil(b/u), where u = 32/8 in little-endian order. + // In other words, we fill up L using u consecutive key bytes + // of K. Any unfilled byte positions in L are zeroed. In the + // case that b = c = 0, set c = 1 and L[0] = 0. + // + int[] L = new int[(key.Length + (4 - 1)) / 4]; + + for (int i = 0; i != key.Length; i++) + { + L[i / 4] += (key[i] & 0xff) << (8 * (i % 4)); + } + + // + // Phase 2: + // Initialize S to a particular fixed pseudo-random bit pattern + // using an arithmetic progression modulo 2^wordsize determined + // by the magic numbers, Pw & Qw. + // + _S = new int[2*(_noRounds + 1)]; + + _S[0] = P32; + for (int i=1; i < _S.Length; i++) + { + _S[i] = (_S[i-1] + Q32); + } + + // + // Phase 3: + // Mix in the user's secret key in 3 passes over the arrays S & L. + // The max of the arrays sizes is used as the loop control + // + int iter; + + if (L.Length > _S.Length) + { + iter = 3 * L.Length; + } + else + { + iter = 3 * _S.Length; + } + + int A = 0, B = 0; + int ii = 0, jj = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + ii = (ii+1) % _S.Length; + jj = (jj+1) % L.Length; + } + } + + /** + * Encrypt the given block starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param in in byte buffer containing data to encrypt + * @param inOff offset into src buffer + * @param out out buffer where encrypted data is written + * @param outOff offset into out buffer + */ + private int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int A = BytesToWord(input, inOff) + _S[0]; + int B = BytesToWord(input, inOff + 4) + _S[1]; + + for (int i = 1; i <= _noRounds; i++) + { + A = RotateLeft(A ^ B, B) + _S[2*i]; + B = RotateLeft(B ^ A, A) + _S[2*i+1]; + } + + WordToBytes(A, outBytes, outOff); + WordToBytes(B, outBytes, outOff + 4); + + return 2 * 4; + } + + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int A = BytesToWord(input, inOff); + int B = BytesToWord(input, inOff + 4); + + for (int i = _noRounds; i >= 1; i--) + { + B = RotateRight(B - _S[2*i+1], A) ^ A; + A = RotateRight(A - _S[2*i], B) ^ B; + } + + WordToBytes(A - _S[0], outBytes, outOff); + WordToBytes(B - _S[1], outBytes, outOff + 4); + + return 2 * 4; + } + + + ////////////////////////////////////////////////////////////// + // + // PRIVATE Helper Methods + // + ////////////////////////////////////////////////////////////// + + /** + * Perform a left "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(32) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + * + * @param x word to rotate + * @param y number of bits to rotate % 32 + */ + private int RotateLeft(int x, int y) { + return ((int) ( (uint) (x << (y & (32-1))) | + ((uint) x >> (32 - (y & (32-1)))) ) + ); + } + + /** + * Perform a right "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(32) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + * + * @param x word to rotate + * @param y number of bits to rotate % 32 + */ + private int RotateRight(int x, int y) { + return ((int) ( ((uint) x >> (y & (32-1))) | + (uint) (x << (32 - (y & (32-1)))) ) + ); + } + + private int BytesToWord( + byte[] src, + int srcOff) + { + return (src[srcOff] & 0xff) | ((src[srcOff + 1] & 0xff) << 8) + | ((src[srcOff + 2] & 0xff) << 16) | ((src[srcOff + 3] & 0xff) << 24); + } + + private void WordToBytes( + int word, + byte[] dst, + int dstOff) + { + dst[dstOff] = (byte)word; + dst[dstOff + 1] = (byte)(word >> 8); + dst[dstOff + 2] = (byte)(word >> 16); + dst[dstOff + 3] = (byte)(word >> 24); + } + } + +} diff --git a/src/core/srcbc/crypto/engines/RC564Engine.cs b/src/core/srcbc/crypto/engines/RC564Engine.cs new file mode 100644 index 0000000..7aee798 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RC564Engine.cs @@ -0,0 +1,295 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * The specification for RC5 came from the RC5 Encryption Algorithm + * publication in RSA CryptoBytes, Spring of 1995. + * http://www.rsasecurity.com/rsalabs/cryptobytes. + *

+ * This implementation is set to work with a 64 bit word size.

+ */ + public class RC564Engine + : IBlockCipher + { + private static readonly int wordSize = 64; + private static readonly int bytesPerWord = wordSize / 8; + + /* + * the number of rounds to perform + */ + private int _noRounds; + + /* + * the expanded key array of size 2*(rounds + 1) + */ + private long [] _S; + + /* + * our "magic constants" for wordSize 62 + * + * Pw = Odd((e-2) * 2^wordsize) + * Qw = Odd((o-2) * 2^wordsize) + * + * where e is the base of natural logarithms (2.718281828...) + * and o is the golden ratio (1.61803398...) + */ + private static readonly long P64 = unchecked( (long) 0xb7e151628aed2a6bL); + private static readonly long Q64 = unchecked( (long) 0x9e3779b97f4a7c15L); + + private bool forEncryption; + + /** + * Create an instance of the RC5 encryption algorithm + * and set some defaults + */ + public RC564Engine() + { + _noRounds = 12; +// _S = null; + } + + public string AlgorithmName + { + get { return "RC5-64"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return 2 * bytesPerWord; + } + + /** + * initialise a RC5-64 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(typeof(RC5Parameters).IsInstanceOfType(parameters))) + { + throw new ArgumentException("invalid parameter passed to RC564 init - " + parameters.GetType().ToString()); + } + + RC5Parameters p = (RC5Parameters)parameters; + + this.forEncryption = forEncryption; + + _noRounds = p.Rounds; + + SetKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @param key the key to be used + */ + private void SetKey( + byte[] key) + { + // + // KEY EXPANSION: + // + // There are 3 phases to the key expansion. + // + // Phase 1: + // Copy the secret key K[0...b-1] into an array L[0..c-1] of + // c = ceil(b/u), where u = wordSize/8 in little-endian order. + // In other words, we fill up L using u consecutive key bytes + // of K. Any unfilled byte positions in L are zeroed. In the + // case that b = c = 0, set c = 1 and L[0] = 0. + // + long[] L = new long[(key.Length + (bytesPerWord - 1)) / bytesPerWord]; + + for (int i = 0; i != key.Length; i++) + { + L[i / bytesPerWord] += (long)(key[i] & 0xff) << (8 * (i % bytesPerWord)); + } + + // + // Phase 2: + // Initialize S to a particular fixed pseudo-random bit pattern + // using an arithmetic progression modulo 2^wordsize determined + // by the magic numbers, Pw & Qw. + // + _S = new long[2*(_noRounds + 1)]; + + _S[0] = P64; + for (int i=1; i < _S.Length; i++) + { + _S[i] = (_S[i-1] + Q64); + } + + // + // Phase 3: + // Mix in the user's secret key in 3 passes over the arrays S & L. + // The max of the arrays sizes is used as the loop control + // + int iter; + + if (L.Length > _S.Length) + { + iter = 3 * L.Length; + } + else + { + iter = 3 * _S.Length; + } + + long A = 0, B = 0; + int ii = 0, jj = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + ii = (ii+1) % _S.Length; + jj = (jj+1) % L.Length; + } + } + + /** + * Encrypt the given block starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * + * @param in in byte buffer containing data to encrypt + * @param inOff offset into src buffer + * @param out out buffer where encrypted data is written + * @param outOff offset into out buffer + */ + private int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + long A = BytesToWord(input, inOff) + _S[0]; + long B = BytesToWord(input, inOff + bytesPerWord) + _S[1]; + + for (int i = 1; i <= _noRounds; i++) + { + A = RotateLeft(A ^ B, B) + _S[2*i]; + B = RotateLeft(B ^ A, A) + _S[2*i+1]; + } + + WordToBytes(A, outBytes, outOff); + WordToBytes(B, outBytes, outOff + bytesPerWord); + + return 2 * bytesPerWord; + } + + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + long A = BytesToWord(input, inOff); + long B = BytesToWord(input, inOff + bytesPerWord); + + for (int i = _noRounds; i >= 1; i--) + { + B = RotateRight(B - _S[2*i+1], A) ^ A; + A = RotateRight(A - _S[2*i], B) ^ B; + } + + WordToBytes(A - _S[0], outBytes, outOff); + WordToBytes(B - _S[1], outBytes, outOff + bytesPerWord); + + return 2 * bytesPerWord; + } + + + ////////////////////////////////////////////////////////////// + // + // PRIVATE Helper Methods + // + ////////////////////////////////////////////////////////////// + + /** + * Perform a left "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + * + * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private long RotateLeft(long x, long y) { + return ((long) ( (ulong) (x << (int) (y & (wordSize-1))) | + ((ulong) x >> (int) (wordSize - (y & (wordSize-1))))) + ); + } + + /** + * Perform a right "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + * + * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private long RotateRight(long x, long y) { + return ((long) ( ((ulong) x >> (int) (y & (wordSize-1))) | + (ulong) (x << (int) (wordSize - (y & (wordSize-1))))) + ); + } + + private long BytesToWord( + byte[] src, + int srcOff) + { + long word = 0; + + for (int i = bytesPerWord - 1; i >= 0; i--) + { + word = (word << 8) + (src[i + srcOff] & 0xff); + } + + return word; + } + + private void WordToBytes( + long word, + byte[] dst, + int dstOff) + { + for (int i = 0; i < bytesPerWord; i++) + { + dst[i + dstOff] = (byte)word; + word = (long) ((ulong) word >> 8); + } + } + } + +} diff --git a/src/core/srcbc/crypto/engines/RC6Engine.cs b/src/core/srcbc/crypto/engines/RC6Engine.cs new file mode 100644 index 0000000..c1c23e0 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RC6Engine.cs @@ -0,0 +1,362 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * An RC6 engine. + */ + public class RC6Engine + : IBlockCipher + { + private static readonly int wordSize = 32; + private static readonly int bytesPerWord = wordSize / 8; + + /* + * the number of rounds to perform + */ + private static readonly int _noRounds = 20; + + /* + * the expanded key array of size 2*(rounds + 1) + */ + private int [] _S; + + /* + * our "magic constants" for wordSize 32 + * + * Pw = Odd((e-2) * 2^wordsize) + * Qw = Odd((o-2) * 2^wordsize) + * + * where e is the base of natural logarithms (2.718281828...) + * and o is the golden ratio (1.61803398...) + */ + private static readonly int P32 = unchecked((int) 0xb7e15163); + private static readonly int Q32 = unchecked((int) 0x9e3779b9); + + private static readonly int LGW = 5; // log2(32) + + private bool forEncryption; + + /** + * Create an instance of the RC6 encryption algorithm + * and set some defaults + */ + public RC6Engine() + { +// _S = null; + } + + public string AlgorithmName + { + get { return "RC6"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return 4 * bytesPerWord; + } + + /** + * initialise a RC5-32 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to RC6 init - " + parameters.GetType().ToString()); + + this.forEncryption = forEncryption; + + KeyParameter p = (KeyParameter)parameters; + SetKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + int blockSize = GetBlockSize(); + if (_S == null) + throw new InvalidOperationException("RC6 engine not initialised"); + if ((inOff + blockSize) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + blockSize) > output.Length) + throw new DataLengthException("output buffer too short"); + + return (forEncryption) + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @param inKey the key to be used + */ + private void SetKey( + byte[] key) + { + // + // KEY EXPANSION: + // + // There are 3 phases to the key expansion. + // + // Phase 1: + // Copy the secret key K[0...b-1] into an array L[0..c-1] of + // c = ceil(b/u), where u = wordSize/8 in little-endian order. + // In other words, we fill up L using u consecutive key bytes + // of K. Any unfilled byte positions in L are zeroed. In the + // case that b = c = 0, set c = 1 and L[0] = 0. + // + // compute number of dwords + int c = (key.Length + (bytesPerWord - 1)) / bytesPerWord; + if (c == 0) + { + c = 1; + } + int[] L = new int[(key.Length + bytesPerWord - 1) / bytesPerWord]; + + // load all key bytes into array of key dwords + for (int i = key.Length - 1; i >= 0; i--) + { + L[i / bytesPerWord] = (L[i / bytesPerWord] << 8) + (key[i] & 0xff); + } + + // + // Phase 2: + // Key schedule is placed in a array of 2+2*ROUNDS+2 = 44 dwords. + // Initialize S to a particular fixed pseudo-random bit pattern + // using an arithmetic progression modulo 2^wordsize determined + // by the magic numbers, Pw & Qw. + // + _S = new int[2+2*_noRounds+2]; + + _S[0] = P32; + for (int i=1; i < _S.Length; i++) + { + _S[i] = (_S[i-1] + Q32); + } + + // + // Phase 3: + // Mix in the user's secret key in 3 passes over the arrays S & L. + // The max of the arrays sizes is used as the loop control + // + int iter; + + if (L.Length > _S.Length) + { + iter = 3 * L.Length; + } + else + { + iter = 3 * _S.Length; + } + + int A = 0; + int B = 0; + int ii = 0, jj = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + ii = (ii+1) % _S.Length; + jj = (jj+1) % L.Length; + } + } + + private int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + // load A,B,C and D registers from in. + int A = BytesToWord(input, inOff); + int B = BytesToWord(input, inOff + bytesPerWord); + int C = BytesToWord(input, inOff + bytesPerWord*2); + int D = BytesToWord(input, inOff + bytesPerWord*3); + + // Do pseudo-round #0: pre-whitening of B and D + B += _S[0]; + D += _S[1]; + + // perform round #1,#2 ... #ROUNDS of encryption + for (int i = 1; i <= _noRounds; i++) + { + int t = 0,u = 0; + + t = B*(2*B+1); + t = RotateLeft(t,5); + + u = D*(2*D+1); + u = RotateLeft(u,5); + + A ^= t; + A = RotateLeft(A,u); + A += _S[2*i]; + + C ^= u; + C = RotateLeft(C,t); + C += _S[2*i+1]; + + int temp = A; + A = B; + B = C; + C = D; + D = temp; + } + // do pseudo-round #(ROUNDS+1) : post-whitening of A and C + A += _S[2*_noRounds+2]; + C += _S[2*_noRounds+3]; + + // store A, B, C and D registers to out + WordToBytes(A, outBytes, outOff); + WordToBytes(B, outBytes, outOff + bytesPerWord); + WordToBytes(C, outBytes, outOff + bytesPerWord*2); + WordToBytes(D, outBytes, outOff + bytesPerWord*3); + + return 4 * bytesPerWord; + } + + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + // load A,B,C and D registers from out. + int A = BytesToWord(input, inOff); + int B = BytesToWord(input, inOff + bytesPerWord); + int C = BytesToWord(input, inOff + bytesPerWord*2); + int D = BytesToWord(input, inOff + bytesPerWord*3); + + // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C + C -= _S[2*_noRounds+3]; + A -= _S[2*_noRounds+2]; + + // Undo round #ROUNDS, .., #2,#1 of encryption + for (int i = _noRounds; i >= 1; i--) + { + int t=0,u = 0; + + int temp = D; + D = C; + C = B; + B = A; + A = temp; + + t = B*(2*B+1); + t = RotateLeft(t, LGW); + + u = D*(2*D+1); + u = RotateLeft(u, LGW); + + C -= _S[2*i+1]; + C = RotateRight(C,t); + C ^= u; + + A -= _S[2*i]; + A = RotateRight(A,u); + A ^= t; + + } + // Undo pseudo-round #0: pre-whitening of B and D + D -= _S[1]; + B -= _S[0]; + + WordToBytes(A, outBytes, outOff); + WordToBytes(B, outBytes, outOff + bytesPerWord); + WordToBytes(C, outBytes, outOff + bytesPerWord*2); + WordToBytes(D, outBytes, outOff + bytesPerWord*3); + + return 4 * bytesPerWord; + } + + + ////////////////////////////////////////////////////////////// + // + // PRIVATE Helper Methods + // + ////////////////////////////////////////////////////////////// + + /** + * Perform a left "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + * + * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private int RotateLeft(int x, int y) + { + return ((int)((uint)(x << (y & (wordSize-1))) + | ((uint) x >> (wordSize - (y & (wordSize-1)))))); + } + + /** + * Perform a right "spin" of the word. The rotation of the given + * word x is rotated left by y bits. + * Only the lg(wordSize) low-order bits of y + * are used to determine the rotation amount. Here it is + * assumed that the wordsize used is a power of 2. + * + * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private int RotateRight(int x, int y) + { + return ((int)(((uint) x >> (y & (wordSize-1))) + | (uint)(x << (wordSize - (y & (wordSize-1)))))); + } + + private int BytesToWord( + byte[] src, + int srcOff) + { + int word = 0; + + for (int i = bytesPerWord - 1; i >= 0; i--) + { + word = (word << 8) + (src[i + srcOff] & 0xff); + } + + return word; + } + + private void WordToBytes( + int word, + byte[] dst, + int dstOff) + { + for (int i = 0; i < bytesPerWord; i++) + { + dst[i + dstOff] = (byte)word; + word = (int) ((uint) word >> 8); + } + } + } + +} diff --git a/src/core/srcbc/crypto/engines/RFC3211WrapEngine.cs b/src/core/srcbc/crypto/engines/RFC3211WrapEngine.cs new file mode 100644 index 0000000..0b537a1 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RFC3211WrapEngine.cs @@ -0,0 +1,166 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of the RFC 3211 Key Wrap + * Specification. + */ + public class Rfc3211WrapEngine + : IWrapper + { + private CbcBlockCipher engine; + private ParametersWithIV param; + private bool forWrapping; + private SecureRandom rand; + + public Rfc3211WrapEngine( + IBlockCipher engine) + { + this.engine = new CbcBlockCipher(engine); + } + + public void Init( + bool forWrapping, + ICipherParameters param) + { + this.forWrapping = forWrapping; + + if (param is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom) param; + + this.rand = p.Random; + this.param = (ParametersWithIV) p.Parameters; + } + else + { + if (forWrapping) + { + rand = new SecureRandom(); + } + + this.param = (ParametersWithIV) param; + } + } + + public string AlgorithmName + { + get { return engine.GetUnderlyingCipher().AlgorithmName + "/RFC3211Wrap"; } + } + + public byte[] Wrap( + byte[] inBytes, + int inOff, + int inLen) + { + if (!forWrapping) + { + throw new InvalidOperationException("not set for wrapping"); + } + + engine.Init(true, param); + + int blockSize = engine.GetBlockSize(); + byte[] cekBlock; + + if (inLen + 4 < blockSize * 2) + { + cekBlock = new byte[blockSize * 2]; + } + else + { + cekBlock = new byte[(inLen + 4) % blockSize == 0 ? inLen + 4 : ((inLen + 4) / blockSize + 1) * blockSize]; + } + + cekBlock[0] = (byte)inLen; + cekBlock[1] = (byte)~inBytes[inOff]; + cekBlock[2] = (byte)~inBytes[inOff + 1]; + cekBlock[3] = (byte)~inBytes[inOff + 2]; + + Array.Copy(inBytes, inOff, cekBlock, 4, inLen); + + rand.NextBytes(cekBlock, inLen + 4, cekBlock.Length - inLen - 4); + + for (int i = 0; i < cekBlock.Length; i += blockSize) + { + engine.ProcessBlock(cekBlock, i, cekBlock, i); + } + + for (int i = 0; i < cekBlock.Length; i += blockSize) + { + engine.ProcessBlock(cekBlock, i, cekBlock, i); + } + + return cekBlock; + } + + public byte[] Unwrap( + byte[] inBytes, + int inOff, + int inLen) + { + if (forWrapping) + { + throw new InvalidOperationException("not set for unwrapping"); + } + + int blockSize = engine.GetBlockSize(); + + if (inLen < 2 * blockSize) + { + throw new InvalidCipherTextException("input too short"); + } + + byte[] cekBlock = new byte[inLen]; + byte[] iv = new byte[blockSize]; + + Array.Copy(inBytes, inOff, cekBlock, 0, inLen); + Array.Copy(inBytes, inOff, iv, 0, iv.Length); + + engine.Init(false, new ParametersWithIV(param.Parameters, iv)); + + for (int i = blockSize; i < cekBlock.Length; i += blockSize) + { + engine.ProcessBlock(cekBlock, i, cekBlock, i); + } + + Array.Copy(cekBlock, cekBlock.Length - iv.Length, iv, 0, iv.Length); + + engine.Init(false, new ParametersWithIV(param.Parameters, iv)); + + engine.ProcessBlock(cekBlock, 0, cekBlock, 0); + + engine.Init(false, param); + + for (int i = 0; i < cekBlock.Length; i += blockSize) + { + engine.ProcessBlock(cekBlock, i, cekBlock, i); + } + + if ((cekBlock[0] & 0xff) > cekBlock.Length - 4) + { + throw new InvalidCipherTextException("wrapped key corrupted"); + } + + byte[] key = new byte[cekBlock[0] & 0xff]; + + Array.Copy(cekBlock, 4, key, 0, cekBlock[0]); + + for (int i = 0; i != 3; i++) + { + byte check = (byte)~cekBlock[1 + i]; + if (check != key[i]) + { + throw new InvalidCipherTextException("wrapped key fails checksum"); + } + } + + return key; + } + } +} diff --git a/src/core/srcbc/crypto/engines/RFC3394WrapEngine.cs b/src/core/srcbc/crypto/engines/RFC3394WrapEngine.cs new file mode 100644 index 0000000..b40efec --- /dev/null +++ b/src/core/srcbc/crypto/engines/RFC3394WrapEngine.cs @@ -0,0 +1,181 @@ +using System; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// + /// An implementation of the AES Key Wrapper from the NIST Key Wrap + /// Specification as described in RFC 3394. + ///

+ /// For further details see: http://www.ietf.org/rfc/rfc3394.txt + /// and http://csrc.nist.gov/encryption/kms/key-wrap.pdf. + /// + public class Rfc3394WrapEngine + : IWrapper + { + private readonly IBlockCipher engine; + + private KeyParameter param; + private bool forWrapping; + + private byte[] iv = + { + 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6 + }; + + public Rfc3394WrapEngine( + IBlockCipher engine) + { + this.engine = engine; + } + + public void Init( + bool forWrapping, + ICipherParameters parameters) + { + this.forWrapping = forWrapping; + + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + if (parameters is KeyParameter) + { + this.param = (KeyParameter) parameters; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV pIV = (ParametersWithIV) parameters; + byte[] iv = pIV.GetIV(); + + if (iv.Length != 8) + throw new ArgumentException("IV length not equal to 8", "parameters"); + + this.iv = iv; + this.param = (KeyParameter) pIV.Parameters; + } + else + { + // TODO Throw an exception for bad parameters? + } + } + + public string AlgorithmName + { + get { return engine.AlgorithmName; } + } + + public byte[] Wrap( + byte[] input, + int inOff, + int inLen) + { + if (!forWrapping) + { + throw new InvalidOperationException("not set for wrapping"); + } + + int n = inLen / 8; + + if ((n * 8) != inLen) + { + throw new DataLengthException("wrap data must be a multiple of 8 bytes"); + } + + byte[] block = new byte[inLen + iv.Length]; + byte[] buf = new byte[8 + iv.Length]; + + Array.Copy(iv, 0, block, 0, iv.Length); + Array.Copy(input, 0, block, iv.Length, inLen); + + engine.Init(true, param); + + for (int j = 0; j != 6; j++) + { + for (int i = 1; i <= n; i++) + { + Array.Copy(block, 0, buf, 0, iv.Length); + Array.Copy(block, 8 * i, buf, iv.Length, 8); + engine.ProcessBlock(buf, 0, buf, 0); + + int t = n * j + i; + for (int k = 1; t != 0; k++) + { + byte v = (byte)t; + + buf[iv.Length - k] ^= v; + t = (int) ((uint)t >> 8); + } + + Array.Copy(buf, 0, block, 0, 8); + Array.Copy(buf, 8, block, 8 * i, 8); + } + } + + return block; + } + + public byte[] Unwrap( + byte[] input, + int inOff, + int inLen) + { + if (forWrapping) + { + throw new InvalidOperationException("not set for unwrapping"); + } + + int n = inLen / 8; + + if ((n * 8) != inLen) + { + throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes"); + } + + byte[] block = new byte[inLen - iv.Length]; + byte[] a = new byte[iv.Length]; + byte[] buf = new byte[8 + iv.Length]; + + Array.Copy(input, 0, a, 0, iv.Length); + Array.Copy(input, iv.Length, block, 0, inLen - iv.Length); + + engine.Init(false, param); + + n = n - 1; + + for (int j = 5; j >= 0; j--) + { + for (int i = n; i >= 1; i--) + { + Array.Copy(a, 0, buf, 0, iv.Length); + Array.Copy(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 = (int) ((uint)t >> 8); + } + + engine.ProcessBlock(buf, 0, buf, 0); + Array.Copy(buf, 0, a, 0, 8); + Array.Copy(buf, 8, block, 8 * (i - 1), 8); + } + } + + for (int i = 0; i != iv.Length; i++) + { + if (a[i] != iv[i]) + { + throw new InvalidCipherTextException("checksum failed"); + } + } + + return block; + } + } +} diff --git a/src/core/srcbc/crypto/engines/RSABlindedEngine.cs b/src/core/srcbc/crypto/engines/RSABlindedEngine.cs new file mode 100644 index 0000000..1dbbe4f --- /dev/null +++ b/src/core/srcbc/crypto/engines/RSABlindedEngine.cs @@ -0,0 +1,139 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * this does your basic RSA algorithm with blinding + */ + public class RsaBlindedEngine + : IAsymmetricBlockCipher + { + private readonly RsaCoreEngine core = new RsaCoreEngine(); + private RsaKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "RSA"; } + } + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters param) + { + core.Init(forEncryption, param); + + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + key = (RsaKeyParameters)rParam.Parameters; + random = rParam.Random; + } + else + { + key = (RsaKeyParameters)param; + random = new SecureRandom(); + } + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int GetInputBlockSize() + { + return core.GetInputBlockSize(); + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int GetOutputBlockSize() + { + return core.GetOutputBlockSize(); + } + + /** + * Process a single block using the basic RSA algorithm. + * + * @param inBuf the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param inLen the length of the data to be processed. + * @return the result of the RSA process. + * @exception DataLengthException the input block is too large. + */ + public byte[] ProcessBlock( + byte[] inBuf, + int inOff, + int inLen) + { + if (key == null) + throw new InvalidOperationException("RSA engine not initialised"); + + BigInteger input = core.ConvertInput(inBuf, inOff, inLen); + + BigInteger result; + if (key is RsaPrivateCrtKeyParameters) + { + RsaPrivateCrtKeyParameters k = (RsaPrivateCrtKeyParameters)key; + if (k.PublicExponent != null) // can't do blinding without a public exponent + { + BigInteger m = k.Modulus; + BigInteger r = calculateR(m); + + BigInteger blindedInput = r.ModPow(k.PublicExponent, m).Multiply(input).Mod(m); + BigInteger blindedResult = core.ProcessBlock(blindedInput); + + result = blindedResult.Multiply(r.ModInverse(m)).Mod(m); + } + else + { + result = core.ProcessBlock(input); + } + } + else + { + result = core.ProcessBlock(input); + } + + return core.ConvertOutput(result); + } + + /* + * calculate a random mess-with-their-heads value. + */ + private BigInteger calculateR( + BigInteger m) + { + int max = m.BitLength - 1; // must be less than m.BitLength + int min = max / 2; + int length = ((random.NextInt() & 0xff) * ((max - min) / 0xff)) + min; + BigInteger factor = new BigInteger(length, random); + + while (factor.SignValue == 0) + { + factor = new BigInteger(length, random); + } + + return factor; + } + } +} diff --git a/src/core/srcbc/crypto/engines/RSABlindingEngine.cs b/src/core/srcbc/crypto/engines/RSABlindingEngine.cs new file mode 100644 index 0000000..22cec15 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RSABlindingEngine.cs @@ -0,0 +1,139 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * This does your basic RSA Chaum's blinding and unblinding as outlined in + * "Handbook of Applied Cryptography", page 475. You need to use this if you are + * trying to get another party to generate signatures without them being aware + * of the message they are signing. + */ + public class RsaBlindingEngine + : IAsymmetricBlockCipher + { + private readonly RsaCoreEngine core = new RsaCoreEngine(); + + private RsaKeyParameters key; + private BigInteger blindingFactor; + + private bool forEncryption; + + public string AlgorithmName + { + get { return "RSA"; } + } + + /** + * Initialise the blinding engine. + * + * @param forEncryption true if we are encrypting (blinding), false otherwise. + * @param param the necessary RSA key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters param) + { + RsaBlindingParameters p; + + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + p = (RsaBlindingParameters)rParam.Parameters; + } + else + { + p = (RsaBlindingParameters)param; + } + + core.Init(forEncryption, p.PublicKey); + + this.forEncryption = forEncryption; + this.key = p.PublicKey; + this.blindingFactor = p.BlindingFactor; + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int GetInputBlockSize() + { + return core.GetInputBlockSize(); + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int GetOutputBlockSize() + { + return core.GetOutputBlockSize(); + } + + /** + * Process a single block using the RSA blinding algorithm. + * + * @param in the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param inLen the length of the data to be processed. + * @return the result of the RSA process. + * @throws DataLengthException the input block is too large. + */ + public byte[] ProcessBlock( + byte[] inBuf, + int inOff, + int inLen) + { + BigInteger msg = core.ConvertInput(inBuf, inOff, inLen); + + if (forEncryption) + { + msg = BlindMessage(msg); + } + else + { + msg = UnblindMessage(msg); + } + + return core.ConvertOutput(msg); + } + + /* + * Blind message with the blind factor. + */ + private BigInteger BlindMessage( + BigInteger msg) + { + BigInteger blindMsg = blindingFactor; + blindMsg = msg.Multiply(blindMsg.ModPow(key.Exponent, key.Modulus)); + blindMsg = blindMsg.Mod(key.Modulus); + + return blindMsg; + } + + /* + * Unblind the message blinded with the blind factor. + */ + private BigInteger UnblindMessage( + BigInteger blindedMsg) + { + BigInteger m = key.Modulus; + BigInteger msg = blindedMsg; + BigInteger blindFactorInverse = blindingFactor.ModInverse(m); + msg = msg.Multiply(blindFactorInverse); + msg = msg.Mod(m); + + return msg; + } + } +} diff --git a/src/core/srcbc/crypto/engines/RSACoreEngine.cs b/src/core/srcbc/crypto/engines/RSACoreEngine.cs new file mode 100644 index 0000000..fe13387 --- /dev/null +++ b/src/core/srcbc/crypto/engines/RSACoreEngine.cs @@ -0,0 +1,156 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * this does your basic RSA algorithm. + */ + class RsaCoreEngine + { + private RsaKeyParameters key; + private bool forEncryption; + private int bitSize; + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + if (!(parameters is RsaKeyParameters)) + throw new InvalidKeyException("Not an RSA key"); + + this.key = (RsaKeyParameters) parameters; + this.forEncryption = forEncryption; + this.bitSize = key.Modulus.BitLength; + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int GetInputBlockSize() + { + if (forEncryption) + { + return (bitSize - 1) / 8; + } + + return (bitSize + 7) / 8; + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int GetOutputBlockSize() + { + if (forEncryption) + { + return (bitSize + 7) / 8; + } + + return (bitSize - 1) / 8; + } + + public BigInteger ConvertInput( + byte[] inBuf, + int inOff, + int inLen) + { + int maxLength = (bitSize + 7) / 8; + + if (inLen > maxLength) + throw new DataLengthException("input too large for RSA cipher."); + + BigInteger input = new BigInteger(1, inBuf, inOff, inLen); + + if (input.CompareTo(key.Modulus) >= 0) + throw new DataLengthException("input too large for RSA cipher."); + + return input; + } + + public byte[] ConvertOutput( + BigInteger result) + { + byte[] output = result.ToByteArrayUnsigned(); + + if (forEncryption) + { + int outSize = GetOutputBlockSize(); + + // TODO To avoid this, create version of BigInteger.ToByteArray that + // writes to an existing array + if (output.Length < outSize) // have ended up with less bytes than normal, lengthen + { + byte[] tmp = new byte[outSize]; + output.CopyTo(tmp, tmp.Length - output.Length); + output = tmp; + } + } + + return output; + } + + public BigInteger ProcessBlock( + BigInteger input) + { + if (key is RsaPrivateCrtKeyParameters) + { + // + // we have the extra factors, use the Chinese Remainder Theorem - the author + // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for + // advice regarding the expression of this. + // + RsaPrivateCrtKeyParameters crtKey = (RsaPrivateCrtKeyParameters)key; + + BigInteger p = crtKey.P;; + BigInteger q = crtKey.Q; + BigInteger dP = crtKey.DP; + BigInteger dQ = crtKey.DQ; + BigInteger qInv = crtKey.QInv; + + BigInteger mP, mQ, h, m; + + // mP = ((input Mod p) ^ dP)) Mod p + mP = (input.Remainder(p)).ModPow(dP, p); + + // mQ = ((input Mod q) ^ dQ)) Mod q + mQ = (input.Remainder(q)).ModPow(dQ, q); + + // h = qInv * (mP - mQ) Mod p + h = mP.Subtract(mQ); + h = h.Multiply(qInv); + h = h.Mod(p); // Mod (in Java) returns the positive residual + + // m = h * q + mQ + m = h.Multiply(q); + m = m.Add(mQ); + + return m; + } + + return input.ModPow(key.Exponent, key.Modulus); + } + } +} diff --git a/src/core/srcbc/crypto/engines/RijndaelEngine.cs b/src/core/srcbc/crypto/engines/RijndaelEngine.cs new file mode 100644 index 0000000..08c890a --- /dev/null +++ b/src/core/srcbc/crypto/engines/RijndaelEngine.cs @@ -0,0 +1,740 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of Rijndael, based on the documentation and reference implementation + * by Paulo Barreto, Vincent Rijmen, for v2.0 August '99. + *

+ * Note: this implementation is based on information prior to readonly NIST publication. + *

+ */ + public class RijndaelEngine + : IBlockCipher + { + private static readonly int MAXROUNDS = 14; + + private static readonly int MAXKC = (256/4); + + private static readonly byte[] Logtable = { + (byte)0, (byte)0, (byte)25, (byte)1, (byte)50, (byte)2, (byte)26, (byte)198, + (byte)75, (byte)199, (byte)27, (byte)104, (byte)51, (byte)238, (byte)223, (byte)3, + (byte)100, (byte)4, (byte)224, (byte)14, (byte)52, (byte)141, (byte)129, (byte)239, + (byte)76, (byte)113, (byte)8, (byte)200, (byte)248, (byte)105, (byte)28, (byte)193, + (byte)125, (byte)194, (byte)29, (byte)181, (byte)249, (byte)185, (byte)39, (byte)106, + (byte)77, (byte)228, (byte)166, (byte)114, (byte)154, (byte)201, (byte)9, (byte)120, + (byte)101, (byte)47, (byte)138, (byte)5, (byte)33, (byte)15, (byte)225, (byte)36, + (byte)18, (byte)240, (byte)130, (byte)69, (byte)53, (byte)147, (byte)218, (byte)142, + (byte)150, (byte)143, (byte)219, (byte)189, (byte)54, (byte)208, (byte)206, (byte)148, + (byte)19, (byte)92, (byte)210, (byte)241, (byte)64, (byte)70, (byte)131, (byte)56, + (byte)102, (byte)221, (byte)253, (byte)48, (byte)191, (byte)6, (byte)139, (byte)98, + (byte)179, (byte)37, (byte)226, (byte)152, (byte)34, (byte)136, (byte)145, (byte)16, + (byte)126, (byte)110, (byte)72, (byte)195, (byte)163, (byte)182, (byte)30, (byte)66, + (byte)58, (byte)107, (byte)40, (byte)84, (byte)250, (byte)133, (byte)61, (byte)186, + (byte)43, (byte)121, (byte)10, (byte)21, (byte)155, (byte)159, (byte)94, (byte)202, + (byte)78, (byte)212, (byte)172, (byte)229, (byte)243, (byte)115, (byte)167, (byte)87, + (byte)175, (byte)88, (byte)168, (byte)80, (byte)244, (byte)234, (byte)214, (byte)116, + (byte)79, (byte)174, (byte)233, (byte)213, (byte)231, (byte)230, (byte)173, (byte)232, + (byte)44, (byte)215, (byte)117, (byte)122, (byte)235, (byte)22, (byte)11, (byte)245, + (byte)89, (byte)203, (byte)95, (byte)176, (byte)156, (byte)169, (byte)81, (byte)160, + (byte)127, (byte)12, (byte)246, (byte)111, (byte)23, (byte)196, (byte)73, (byte)236, + (byte)216, (byte)67, (byte)31, (byte)45, (byte)164, (byte)118, (byte)123, (byte)183, + (byte)204, (byte)187, (byte)62, (byte)90, (byte)251, (byte)96, (byte)177, (byte)134, + (byte)59, (byte)82, (byte)161, (byte)108, (byte)170, (byte)85, (byte)41, (byte)157, + (byte)151, (byte)178, (byte)135, (byte)144, (byte)97, (byte)190, (byte)220, (byte)252, + (byte)188, (byte)149, (byte)207, (byte)205, (byte)55, (byte)63, (byte)91, (byte)209, + (byte)83, (byte)57, (byte)132, (byte)60, (byte)65, (byte)162, (byte)109, (byte)71, + (byte)20, (byte)42, (byte)158, (byte)93, (byte)86, (byte)242, (byte)211, (byte)171, + (byte)68, (byte)17, (byte)146, (byte)217, (byte)35, (byte)32, (byte)46, (byte)137, + (byte)180, (byte)124, (byte)184, (byte)38, (byte)119, (byte)153, (byte)227, (byte)165, + (byte)103, (byte)74, (byte)237, (byte)222, (byte)197, (byte)49, (byte)254, (byte)24, + (byte)13, (byte)99, (byte)140, (byte)128, (byte)192, (byte)247, (byte)112, (byte)7 + }; + + private static readonly byte[] Alogtable = { + (byte)0, (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53, + (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170, + (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49, + (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205, + (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136, + (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154, + (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163, + (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160, + (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65, + (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117, + (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128, + (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84, + (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202, + (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14, + (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23, + (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1, + (byte)3, (byte)5, (byte)15, (byte)17, (byte)51, (byte)85, (byte)255, (byte)26, (byte)46, (byte)114, (byte)150, (byte)161, (byte)248, (byte)19, (byte)53, + (byte)95, (byte)225, (byte)56, (byte)72, (byte)216, (byte)115, (byte)149, (byte)164, (byte)247, (byte)2, (byte)6, (byte)10, (byte)30, (byte)34, (byte)102, (byte)170, + (byte)229, (byte)52, (byte)92, (byte)228, (byte)55, (byte)89, (byte)235, (byte)38, (byte)106, (byte)190, (byte)217, (byte)112, (byte)144, (byte)171, (byte)230, (byte)49, + (byte)83, (byte)245, (byte)4, (byte)12, (byte)20, (byte)60, (byte)68, (byte)204, (byte)79, (byte)209, (byte)104, (byte)184, (byte)211, (byte)110, (byte)178, (byte)205, + (byte)76, (byte)212, (byte)103, (byte)169, (byte)224, (byte)59, (byte)77, (byte)215, (byte)98, (byte)166, (byte)241, (byte)8, (byte)24, (byte)40, (byte)120, (byte)136, + (byte)131, (byte)158, (byte)185, (byte)208, (byte)107, (byte)189, (byte)220, (byte)127, (byte)129, (byte)152, (byte)179, (byte)206, (byte)73, (byte)219, (byte)118, (byte)154, + (byte)181, (byte)196, (byte)87, (byte)249, (byte)16, (byte)48, (byte)80, (byte)240, (byte)11, (byte)29, (byte)39, (byte)105, (byte)187, (byte)214, (byte)97, (byte)163, + (byte)254, (byte)25, (byte)43, (byte)125, (byte)135, (byte)146, (byte)173, (byte)236, (byte)47, (byte)113, (byte)147, (byte)174, (byte)233, (byte)32, (byte)96, (byte)160, + (byte)251, (byte)22, (byte)58, (byte)78, (byte)210, (byte)109, (byte)183, (byte)194, (byte)93, (byte)231, (byte)50, (byte)86, (byte)250, (byte)21, (byte)63, (byte)65, + (byte)195, (byte)94, (byte)226, (byte)61, (byte)71, (byte)201, (byte)64, (byte)192, (byte)91, (byte)237, (byte)44, (byte)116, (byte)156, (byte)191, (byte)218, (byte)117, + (byte)159, (byte)186, (byte)213, (byte)100, (byte)172, (byte)239, (byte)42, (byte)126, (byte)130, (byte)157, (byte)188, (byte)223, (byte)122, (byte)142, (byte)137, (byte)128, + (byte)155, (byte)182, (byte)193, (byte)88, (byte)232, (byte)35, (byte)101, (byte)175, (byte)234, (byte)37, (byte)111, (byte)177, (byte)200, (byte)67, (byte)197, (byte)84, + (byte)252, (byte)31, (byte)33, (byte)99, (byte)165, (byte)244, (byte)7, (byte)9, (byte)27, (byte)45, (byte)119, (byte)153, (byte)176, (byte)203, (byte)70, (byte)202, + (byte)69, (byte)207, (byte)74, (byte)222, (byte)121, (byte)139, (byte)134, (byte)145, (byte)168, (byte)227, (byte)62, (byte)66, (byte)198, (byte)81, (byte)243, (byte)14, + (byte)18, (byte)54, (byte)90, (byte)238, (byte)41, (byte)123, (byte)141, (byte)140, (byte)143, (byte)138, (byte)133, (byte)148, (byte)167, (byte)242, (byte)13, (byte)23, + (byte)57, (byte)75, (byte)221, (byte)124, (byte)132, (byte)151, (byte)162, (byte)253, (byte)28, (byte)36, (byte)108, (byte)180, (byte)199, (byte)82, (byte)246, (byte)1, + }; + + private static readonly byte[] S = { + (byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197, (byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118, + (byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240, (byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192, + (byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204, (byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21, + (byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154, (byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117, + (byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160, (byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132, + (byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91, (byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207, + (byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133, (byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168, + (byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245, (byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210, + (byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23, (byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115, + (byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136, (byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219, + (byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92, (byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121, + (byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169, (byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8, + (byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198, (byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138, + (byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14, (byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158, + (byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148, (byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223, + (byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104, (byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22, + }; + + private static readonly byte[] Si = { + (byte)82, (byte)9, (byte)106, (byte)213, (byte)48, (byte)54, (byte)165, (byte)56, (byte)191, (byte)64, (byte)163, (byte)158, (byte)129, (byte)243, (byte)215, (byte)251, + (byte)124, (byte)227, (byte)57, (byte)130, (byte)155, (byte)47, (byte)255, (byte)135, (byte)52, (byte)142, (byte)67, (byte)68, (byte)196, (byte)222, (byte)233, (byte)203, + (byte)84, (byte)123, (byte)148, (byte)50, (byte)166, (byte)194, (byte)35, (byte)61, (byte)238, (byte)76, (byte)149, (byte)11, (byte)66, (byte)250, (byte)195, (byte)78, + (byte)8, (byte)46, (byte)161, (byte)102, (byte)40, (byte)217, (byte)36, (byte)178, (byte)118, (byte)91, (byte)162, (byte)73, (byte)109, (byte)139, (byte)209, (byte)37, + (byte)114, (byte)248, (byte)246, (byte)100, (byte)134, (byte)104, (byte)152, (byte)22, (byte)212, (byte)164, (byte)92, (byte)204, (byte)93, (byte)101, (byte)182, (byte)146, + (byte)108, (byte)112, (byte)72, (byte)80, (byte)253, (byte)237, (byte)185, (byte)218, (byte)94, (byte)21, (byte)70, (byte)87, (byte)167, (byte)141, (byte)157, (byte)132, + (byte)144, (byte)216, (byte)171, (byte)0, (byte)140, (byte)188, (byte)211, (byte)10, (byte)247, (byte)228, (byte)88, (byte)5, (byte)184, (byte)179, (byte)69, (byte)6, + (byte)208, (byte)44, (byte)30, (byte)143, (byte)202, (byte)63, (byte)15, (byte)2, (byte)193, (byte)175, (byte)189, (byte)3, (byte)1, (byte)19, (byte)138, (byte)107, + (byte)58, (byte)145, (byte)17, (byte)65, (byte)79, (byte)103, (byte)220, (byte)234, (byte)151, (byte)242, (byte)207, (byte)206, (byte)240, (byte)180, (byte)230, (byte)115, + (byte)150, (byte)172, (byte)116, (byte)34, (byte)231, (byte)173, (byte)53, (byte)133, (byte)226, (byte)249, (byte)55, (byte)232, (byte)28, (byte)117, (byte)223, (byte)110, + (byte)71, (byte)241, (byte)26, (byte)113, (byte)29, (byte)41, (byte)197, (byte)137, (byte)111, (byte)183, (byte)98, (byte)14, (byte)170, (byte)24, (byte)190, (byte)27, + (byte)252, (byte)86, (byte)62, (byte)75, (byte)198, (byte)210, (byte)121, (byte)32, (byte)154, (byte)219, (byte)192, (byte)254, (byte)120, (byte)205, (byte)90, (byte)244, + (byte)31, (byte)221, (byte)168, (byte)51, (byte)136, (byte)7, (byte)199, (byte)49, (byte)177, (byte)18, (byte)16, (byte)89, (byte)39, (byte)128, (byte)236, (byte)95, + (byte)96, (byte)81, (byte)127, (byte)169, (byte)25, (byte)181, (byte)74, (byte)13, (byte)45, (byte)229, (byte)122, (byte)159, (byte)147, (byte)201, (byte)156, (byte)239, + (byte)160, (byte)224, (byte)59, (byte)77, (byte)174, (byte)42, (byte)245, (byte)176, (byte)200, (byte)235, (byte)187, (byte)60, (byte)131, (byte)83, (byte)153, (byte)97, + (byte)23, (byte)43, (byte)4, (byte)126, (byte)186, (byte)119, (byte)214, (byte)38, (byte)225, (byte)105, (byte)20, (byte)99, (byte)85, (byte)33, (byte)12, (byte)125, + }; + + private static readonly int[] rcon = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; + + static readonly byte[][] shifts0 = new byte [][] + { + new byte [] { 0, 8, 16, 24 }, + new byte [] { 0, 8, 16, 24 }, + new byte [] { 0, 8, 16, 24 }, + new byte [] { 0, 8, 16, 32 }, + new byte [] { 0, 8, 24, 32 } + }; + + static readonly byte[][] shifts1 = + { + new byte [] { 0, 24, 16, 8 }, + new byte [] { 0, 32, 24, 16 }, + new byte [] { 0, 40, 32, 24 }, + new byte [] { 0, 48, 40, 24 }, + new byte [] { 0, 56, 40, 32 } + }; + + /** + * multiply two elements of GF(2^m) + * needed for MixColumn and InvMixColumn + */ + private byte Mul0x2( + int b) + { + if (b != 0) + { + return Alogtable[25 + (Logtable[b] & 0xff)]; + } + else + { + return 0; + } + } + + private byte Mul0x3( + int b) + { + if (b != 0) + { + return Alogtable[1 + (Logtable[b] & 0xff)]; + } + else + { + return 0; + } + } + + private byte Mul0x9( + int b) + { + if (b >= 0) + { + return Alogtable[199 + b]; + } + else + { + return 0; + } + } + + private byte Mul0xb( + int b) + { + if (b >= 0) + { + return Alogtable[104 + b]; + } + else + { + return 0; + } + } + + private byte Mul0xd( + int b) + { + if (b >= 0) + { + return Alogtable[238 + b]; + } + else + { + return 0; + } + } + + private byte Mul0xe( + int b) + { + if (b >= 0) + { + return Alogtable[223 + b]; + } + else + { + return 0; + } + } + + /** + * xor corresponding text input and round key input bytes + */ + private void KeyAddition( + long[] rk) + { + A0 ^= rk[0]; + A1 ^= rk[1]; + A2 ^= rk[2]; + A3 ^= rk[3]; + } + + private long Shift( + long r, + int shift) + { + //return (((long)((ulong) r >> shift) | (r << (BC - shift)))) & BC_MASK; + + ulong temp = (ulong) r >> shift; + + // NB: This corrects for Mono Bug #79087 (fixed in 1.1.17) + if (shift > 31) + { + temp &= 0xFFFFFFFFUL; + } + + return ((long) temp | (r << (BC - shift))) & BC_MASK; + } + + /** + * Row 0 remains unchanged + * The other three rows are shifted a variable amount + */ + private void ShiftRow( + byte[] shiftsSC) + { + A1 = Shift(A1, shiftsSC[1]); + A2 = Shift(A2, shiftsSC[2]); + A3 = Shift(A3, shiftsSC[3]); + } + + private long ApplyS( + long r, + byte[] box) + { + long res = 0; + + for (int j = 0; j < BC; j += 8) + { + res |= (long)(box[(int)((r >> j) & 0xff)] & 0xff) << j; + } + + return res; + } + + /** + * Replace every byte of the input by the byte at that place + * in the nonlinear S-box + */ + private void Substitution( + byte[] box) + { + A0 = ApplyS(A0, box); + A1 = ApplyS(A1, box); + A2 = ApplyS(A2, box); + A3 = ApplyS(A3, box); + } + + /** + * Mix the bytes of every column in a linear way + */ + private void MixColumn() + { + long r0, r1, r2, r3; + + r0 = r1 = r2 = r3 = 0; + + for (int j = 0; j < BC; j += 8) + { + int a0 = (int)((A0 >> j) & 0xff); + int a1 = (int)((A1 >> j) & 0xff); + int a2 = (int)((A2 >> j) & 0xff); + int a3 = (int)((A3 >> j) & 0xff); + + r0 |= (long)((Mul0x2(a0) ^ Mul0x3(a1) ^ a2 ^ a3) & 0xff) << j; + + r1 |= (long)((Mul0x2(a1) ^ Mul0x3(a2) ^ a3 ^ a0) & 0xff) << j; + + r2 |= (long)((Mul0x2(a2) ^ Mul0x3(a3) ^ a0 ^ a1) & 0xff) << j; + + r3 |= (long)((Mul0x2(a3) ^ Mul0x3(a0) ^ a1 ^ a2) & 0xff) << j; + } + + A0 = r0; + A1 = r1; + A2 = r2; + A3 = r3; + } + + /** + * Mix the bytes of every column in a linear way + * This is the opposite operation of Mixcolumn + */ + private void InvMixColumn() + { + long r0, r1, r2, r3; + + r0 = r1 = r2 = r3 = 0; + for (int j = 0; j < BC; j += 8) + { + int a0 = (int)((A0 >> j) & 0xff); + int a1 = (int)((A1 >> j) & 0xff); + int a2 = (int)((A2 >> j) & 0xff); + int a3 = (int)((A3 >> j) & 0xff); + + // + // pre-lookup the log table + // + a0 = (a0 != 0) ? (Logtable[a0 & 0xff] & 0xff) : -1; + a1 = (a1 != 0) ? (Logtable[a1 & 0xff] & 0xff) : -1; + a2 = (a2 != 0) ? (Logtable[a2 & 0xff] & 0xff) : -1; + a3 = (a3 != 0) ? (Logtable[a3 & 0xff] & 0xff) : -1; + + r0 |= (long)((Mul0xe(a0) ^ Mul0xb(a1) ^ Mul0xd(a2) ^ Mul0x9(a3)) & 0xff) << j; + + r1 |= (long)((Mul0xe(a1) ^ Mul0xb(a2) ^ Mul0xd(a3) ^ Mul0x9(a0)) & 0xff) << j; + + r2 |= (long)((Mul0xe(a2) ^ Mul0xb(a3) ^ Mul0xd(a0) ^ Mul0x9(a1)) & 0xff) << j; + + r3 |= (long)((Mul0xe(a3) ^ Mul0xb(a0) ^ Mul0xd(a1) ^ Mul0x9(a2)) & 0xff) << j; + } + + A0 = r0; + A1 = r1; + A2 = r2; + A3 = r3; + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on keyBits and blockBits + */ + private long[][] GenerateWorkingKey( + byte[] key) + { + int KC; + int t, rconpointer = 0; + int keyBits = key.Length * 8; + byte[,] tk = new byte[4,MAXKC]; + //long[,] W = new long[MAXROUNDS+1,4]; + long[][] W = new long[MAXROUNDS+1][]; + + for (int i = 0; i < MAXROUNDS+1; i++) W[i] = new long[4]; + + switch (keyBits) + { + case 128: + KC = 4; + break; + case 160: + KC = 5; + break; + case 192: + KC = 6; + break; + case 224: + KC = 7; + break; + case 256: + KC = 8; + break; + default : + throw new ArgumentException("Key length not 128/160/192/224/256 bits."); + } + + if (keyBits >= blockBits) + { + ROUNDS = KC + 6; + } + else + { + ROUNDS = (BC / 8) + 6; + } + + // + // copy the key into the processing area + // + int index = 0; + + for (int i = 0; i < key.Length; i++) + { + tk[i % 4,i / 4] = key[index++]; + } + + t = 0; + + // + // copy values into round key array + // + for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC / 8)); j++, t++) + { + for (int i = 0; i < 4; i++) + { + W[t / (BC / 8)][i] |= (long)(tk[i,j] & 0xff) << ((t * 8) % BC); + } + } + + // + // while not enough round key material calculated + // calculate new values + // + while (t < (ROUNDS+1)*(BC/8)) + { + for (int i = 0; i < 4; i++) + { + tk[i,0] ^= S[tk[(i+1)%4,KC-1] & 0xff]; + } + tk[0,0] ^= (byte) rcon[rconpointer++]; + + if (KC <= 6) + { + for (int j = 1; j < KC; j++) + { + for (int i = 0; i < 4; i++) + { + tk[i,j] ^= tk[i,j-1]; + } + } + } + else + { + for (int j = 1; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + tk[i,j] ^= tk[i,j-1]; + } + } + for (int i = 0; i < 4; i++) + { + tk[i,4] ^= S[tk[i,3] & 0xff]; + } + for (int j = 5; j < KC; j++) + { + for (int i = 0; i < 4; i++) + { + tk[i,j] ^= tk[i,j-1]; + } + } + } + + // + // copy values into round key array + // + for (int j = 0; (j < KC) && (t < (ROUNDS+1)*(BC/8)); j++, t++) + { + for (int i = 0; i < 4; i++) + { + W[t / (BC/8)][i] |= (long)(tk[i,j] & 0xff) << ((t * 8) % (BC)); + } + } + } + return W; + } + + private int BC; + private long BC_MASK; + private int ROUNDS; + private int blockBits; + private long[][] workingKey; + private long A0, A1, A2, A3; + private bool forEncryption; + private byte[] shifts0SC; + private byte[] shifts1SC; + + /** + * default constructor - 128 bit block size. + */ + public RijndaelEngine() : this(128) {} + + /** + * basic constructor - set the cipher up for a given blocksize + * + * @param blocksize the blocksize in bits, must be 128, 192, or 256. + */ + public RijndaelEngine( + int blockBits) + { + switch (blockBits) + { + case 128: + BC = 32; + BC_MASK = 0xffffffffL; + shifts0SC = shifts0[0]; + shifts1SC = shifts1[0]; + break; + case 160: + BC = 40; + BC_MASK = 0xffffffffffL; + shifts0SC = shifts0[1]; + shifts1SC = shifts1[1]; + break; + case 192: + BC = 48; + BC_MASK = 0xffffffffffffL; + shifts0SC = shifts0[2]; + shifts1SC = shifts1[2]; + break; + case 224: + BC = 56; + BC_MASK = 0xffffffffffffffL; + shifts0SC = shifts0[3]; + shifts1SC = shifts1[3]; + break; + case 256: + BC = 64; + BC_MASK = unchecked( (long)0xffffffffffffffffL); + shifts0SC = shifts0[4]; + shifts1SC = shifts1[4]; + break; + default: + throw new ArgumentException("unknown blocksize to Rijndael"); + } + + this.blockBits = blockBits; + } + + /** + * initialise a Rijndael cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (typeof(KeyParameter).IsInstanceOfType(parameters)) + { + workingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey()); + this.forEncryption = forEncryption; + return; + } + + throw new ArgumentException("invalid parameter passed to Rijndael init - " + parameters.GetType().ToString()); + } + + public string AlgorithmName + { + get { return "Rijndael"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BC / 2; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + { + throw new InvalidOperationException("Rijndael engine not initialised"); + } + + if ((inOff + (BC / 2)) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (BC / 2)) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + UnPackBlock(input, inOff); + + if (forEncryption) + { + EncryptBlock(workingKey); + } + else + { + DecryptBlock(workingKey); + } + + PackBlock(output, outOff); + + return BC / 2; + } + + public void Reset() + { + } + + private void UnPackBlock( + byte[] bytes, + int off) + { + int index = off; + + A0 = (long)(bytes[index++] & 0xff); + A1 = (long)(bytes[index++] & 0xff); + A2 = (long)(bytes[index++] & 0xff); + A3 = (long)(bytes[index++] & 0xff); + + for (int j = 8; j != BC; j += 8) + { + A0 |= (long)(bytes[index++] & 0xff) << j; + A1 |= (long)(bytes[index++] & 0xff) << j; + A2 |= (long)(bytes[index++] & 0xff) << j; + A3 |= (long)(bytes[index++] & 0xff) << j; + } + } + + private void PackBlock( + byte[] bytes, + int off) + { + int index = off; + + for (int j = 0; j != BC; j += 8) + { + bytes[index++] = (byte)(A0 >> j); + bytes[index++] = (byte)(A1 >> j); + bytes[index++] = (byte)(A2 >> j); + bytes[index++] = (byte)(A3 >> j); + } + } + + private void EncryptBlock( + long[][] rk) + { + int r; + + // + // begin with a key addition + // + KeyAddition(rk[0]); + + // + // ROUNDS-1 ordinary rounds + // + for (r = 1; r < ROUNDS; r++) + { + Substitution(S); + ShiftRow(shifts0SC); + MixColumn(); + KeyAddition(rk[r]); + } + + // + // Last round is special: there is no MixColumn + // + Substitution(S); + ShiftRow(shifts0SC); + KeyAddition(rk[ROUNDS]); + } + + private void DecryptBlock( + long[][] rk) + { + int r; + + // To decrypt: apply the inverse operations of the encrypt routine, + // in opposite order + // + // (KeyAddition is an involution: it 's equal to its inverse) + // (the inverse of Substitution with table S is Substitution with the inverse table of S) + // (the inverse of Shiftrow is Shiftrow over a suitable distance) + // + + // First the special round: + // without InvMixColumn + // with extra KeyAddition + // + KeyAddition(rk[ROUNDS]); + Substitution(Si); + ShiftRow(shifts1SC); + + // + // ROUNDS-1 ordinary rounds + // + for (r = ROUNDS-1; r > 0; r--) + { + KeyAddition(rk[r]); + InvMixColumn(); + Substitution(Si); + ShiftRow(shifts1SC); + } + + // + // End with the extra key addition + // + KeyAddition(rk[0]); + } + } + +} diff --git a/src/core/srcbc/crypto/engines/RsaEngine.cs b/src/core/srcbc/crypto/engines/RsaEngine.cs new file mode 100644 index 0000000..91fae9f --- /dev/null +++ b/src/core/srcbc/crypto/engines/RsaEngine.cs @@ -0,0 +1,78 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * this does your basic RSA algorithm. + */ + public class RsaEngine + : IAsymmetricBlockCipher + { + private RsaCoreEngine core; + + public string AlgorithmName + { + get { return "RSA"; } + } + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (core == null) + core = new RsaCoreEngine(); + + core.Init(forEncryption, parameters); + } + + /** + * Return the maximum size for an input block to this engine. + * For RSA this is always one byte less than the key size on + * encryption, and the same length as the key size on decryption. + * + * @return maximum size for an input block. + */ + public int GetInputBlockSize() + { + return core.GetInputBlockSize(); + } + + /** + * Return the maximum size for an output block to this engine. + * For RSA this is always one byte less than the key size on + * decryption, and the same length as the key size on encryption. + * + * @return maximum size for an output block. + */ + public int GetOutputBlockSize() + { + return core.GetOutputBlockSize(); + } + + /** + * Process a single block using the basic RSA algorithm. + * + * @param inBuf the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param inLen the length of the data to be processed. + * @return the result of the RSA process. + * @exception DataLengthException the input block is too large. + */ + public byte[] ProcessBlock( + byte[] inBuf, + int inOff, + int inLen) + { + if (core == null) + throw new InvalidOperationException("RSA engine not initialised"); + + return core.ConvertOutput(core.ProcessBlock(core.ConvertInput(inBuf, inOff, inLen))); + } + } +} diff --git a/src/core/srcbc/crypto/engines/SEEDEngine.cs b/src/core/srcbc/crypto/engines/SEEDEngine.cs new file mode 100644 index 0000000..6e66dbb --- /dev/null +++ b/src/core/srcbc/crypto/engines/SEEDEngine.cs @@ -0,0 +1,361 @@ +using System; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Implementation of the SEED algorithm as described in RFC 4009 + */ + public class SeedEngine + : IBlockCipher + { + private const int BlockSize = 16; + + private static readonly uint[] SS0 = + { + 0x2989a1a8, 0x05858184, 0x16c6d2d4, 0x13c3d3d0, 0x14445054, 0x1d0d111c, 0x2c8ca0ac, 0x25052124, + 0x1d4d515c, 0x03434340, 0x18081018, 0x1e0e121c, 0x11415150, 0x3cccf0fc, 0x0acac2c8, 0x23436360, + 0x28082028, 0x04444044, 0x20002020, 0x1d8d919c, 0x20c0e0e0, 0x22c2e2e0, 0x08c8c0c8, 0x17071314, + 0x2585a1a4, 0x0f8f838c, 0x03030300, 0x3b4b7378, 0x3b8bb3b8, 0x13031310, 0x12c2d2d0, 0x2ecee2ec, + 0x30407070, 0x0c8c808c, 0x3f0f333c, 0x2888a0a8, 0x32023230, 0x1dcdd1dc, 0x36c6f2f4, 0x34447074, + 0x2ccce0ec, 0x15859194, 0x0b0b0308, 0x17475354, 0x1c4c505c, 0x1b4b5358, 0x3d8db1bc, 0x01010100, + 0x24042024, 0x1c0c101c, 0x33437370, 0x18889098, 0x10001010, 0x0cccc0cc, 0x32c2f2f0, 0x19c9d1d8, + 0x2c0c202c, 0x27c7e3e4, 0x32427270, 0x03838380, 0x1b8b9398, 0x11c1d1d0, 0x06868284, 0x09c9c1c8, + 0x20406060, 0x10405050, 0x2383a3a0, 0x2bcbe3e8, 0x0d0d010c, 0x3686b2b4, 0x1e8e929c, 0x0f4f434c, + 0x3787b3b4, 0x1a4a5258, 0x06c6c2c4, 0x38487078, 0x2686a2a4, 0x12021210, 0x2f8fa3ac, 0x15c5d1d4, + 0x21416160, 0x03c3c3c0, 0x3484b0b4, 0x01414140, 0x12425250, 0x3d4d717c, 0x0d8d818c, 0x08080008, + 0x1f0f131c, 0x19899198, 0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37c7f3f4, 0x21c1e1e0, + 0x3dcdf1fc, 0x36467274, 0x2f0f232c, 0x27072324, 0x3080b0b0, 0x0b8b8388, 0x0e0e020c, 0x2b8ba3a8, + 0x2282a2a0, 0x2e4e626c, 0x13839390, 0x0d4d414c, 0x29496168, 0x3c4c707c, 0x09090108, 0x0a0a0208, + 0x3f8fb3bc, 0x2fcfe3ec, 0x33c3f3f0, 0x05c5c1c4, 0x07878384, 0x14041014, 0x3ecef2fc, 0x24446064, + 0x1eced2dc, 0x2e0e222c, 0x0b4b4348, 0x1a0a1218, 0x06060204, 0x21012120, 0x2b4b6368, 0x26466264, + 0x02020200, 0x35c5f1f4, 0x12829290, 0x0a8a8288, 0x0c0c000c, 0x3383b3b0, 0x3e4e727c, 0x10c0d0d0, + 0x3a4a7278, 0x07474344, 0x16869294, 0x25c5e1e4, 0x26062224, 0x00808080, 0x2d8da1ac, 0x1fcfd3dc, + 0x2181a1a0, 0x30003030, 0x37073334, 0x2e8ea2ac, 0x36063234, 0x15051114, 0x22022220, 0x38083038, + 0x34c4f0f4, 0x2787a3a4, 0x05454144, 0x0c4c404c, 0x01818180, 0x29c9e1e8, 0x04848084, 0x17879394, + 0x35053134, 0x0bcbc3c8, 0x0ecec2cc, 0x3c0c303c, 0x31417170, 0x11011110, 0x07c7c3c4, 0x09898188, + 0x35457174, 0x3bcbf3f8, 0x1acad2d8, 0x38c8f0f8, 0x14849094, 0x19495158, 0x02828280, 0x04c4c0c4, + 0x3fcff3fc, 0x09494148, 0x39093138, 0x27476364, 0x00c0c0c0, 0x0fcfc3cc, 0x17c7d3d4, 0x3888b0b8, + 0x0f0f030c, 0x0e8e828c, 0x02424240, 0x23032320, 0x11819190, 0x2c4c606c, 0x1bcbd3d8, 0x2484a0a4, + 0x34043034, 0x31c1f1f0, 0x08484048, 0x02c2c2c0, 0x2f4f636c, 0x3d0d313c, 0x2d0d212c, 0x00404040, + 0x3e8eb2bc, 0x3e0e323c, 0x3c8cb0bc, 0x01c1c1c0, 0x2a8aa2a8, 0x3a8ab2b8, 0x0e4e424c, 0x15455154, + 0x3b0b3338, 0x1cccd0dc, 0x28486068, 0x3f4f737c, 0x1c8c909c, 0x18c8d0d8, 0x0a4a4248, 0x16465254, + 0x37477374, 0x2080a0a0, 0x2dcde1ec, 0x06464244, 0x3585b1b4, 0x2b0b2328, 0x25456164, 0x3acaf2f8, + 0x23c3e3e0, 0x3989b1b8, 0x3181b1b0, 0x1f8f939c, 0x1e4e525c, 0x39c9f1f8, 0x26c6e2e4, 0x3282b2b0, + 0x31013130, 0x2acae2e8, 0x2d4d616c, 0x1f4f535c, 0x24c4e0e4, 0x30c0f0f0, 0x0dcdc1cc, 0x08888088, + 0x16061214, 0x3a0a3238, 0x18485058, 0x14c4d0d4, 0x22426260, 0x29092128, 0x07070304, 0x33033330, + 0x28c8e0e8, 0x1b0b1318, 0x05050104, 0x39497178, 0x10809090, 0x2a4a6268, 0x2a0a2228, 0x1a8a9298 + }; + + private static readonly uint[] SS1 = + { + 0x38380830, 0xe828c8e0, 0x2c2d0d21, 0xa42686a2, 0xcc0fcfc3, 0xdc1eced2, 0xb03383b3, 0xb83888b0, + 0xac2f8fa3, 0x60204060, 0x54154551, 0xc407c7c3, 0x44044440, 0x6c2f4f63, 0x682b4b63, 0x581b4b53, + 0xc003c3c3, 0x60224262, 0x30330333, 0xb43585b1, 0x28290921, 0xa02080a0, 0xe022c2e2, 0xa42787a3, + 0xd013c3d3, 0x90118191, 0x10110111, 0x04060602, 0x1c1c0c10, 0xbc3c8cb0, 0x34360632, 0x480b4b43, + 0xec2fcfe3, 0x88088880, 0x6c2c4c60, 0xa82888a0, 0x14170713, 0xc404c4c0, 0x14160612, 0xf434c4f0, + 0xc002c2c2, 0x44054541, 0xe021c1e1, 0xd416c6d2, 0x3c3f0f33, 0x3c3d0d31, 0x8c0e8e82, 0x98188890, + 0x28280820, 0x4c0e4e42, 0xf436c6f2, 0x3c3e0e32, 0xa42585a1, 0xf839c9f1, 0x0c0d0d01, 0xdc1fcfd3, + 0xd818c8d0, 0x282b0b23, 0x64264662, 0x783a4a72, 0x24270723, 0x2c2f0f23, 0xf031c1f1, 0x70324272, + 0x40024242, 0xd414c4d0, 0x40014141, 0xc000c0c0, 0x70334373, 0x64274763, 0xac2c8ca0, 0x880b8b83, + 0xf437c7f3, 0xac2d8da1, 0x80008080, 0x1c1f0f13, 0xc80acac2, 0x2c2c0c20, 0xa82a8aa2, 0x34340430, + 0xd012c2d2, 0x080b0b03, 0xec2ecee2, 0xe829c9e1, 0x5c1d4d51, 0x94148490, 0x18180810, 0xf838c8f0, + 0x54174753, 0xac2e8ea2, 0x08080800, 0xc405c5c1, 0x10130313, 0xcc0dcdc1, 0x84068682, 0xb83989b1, + 0xfc3fcff3, 0x7c3d4d71, 0xc001c1c1, 0x30310131, 0xf435c5f1, 0x880a8a82, 0x682a4a62, 0xb03181b1, + 0xd011c1d1, 0x20200020, 0xd417c7d3, 0x00020202, 0x20220222, 0x04040400, 0x68284860, 0x70314171, + 0x04070703, 0xd81bcbd3, 0x9c1d8d91, 0x98198991, 0x60214161, 0xbc3e8eb2, 0xe426c6e2, 0x58194951, + 0xdc1dcdd1, 0x50114151, 0x90108090, 0xdc1cccd0, 0x981a8a92, 0xa02383a3, 0xa82b8ba3, 0xd010c0d0, + 0x80018181, 0x0c0f0f03, 0x44074743, 0x181a0a12, 0xe023c3e3, 0xec2ccce0, 0x8c0d8d81, 0xbc3f8fb3, + 0x94168692, 0x783b4b73, 0x5c1c4c50, 0xa02282a2, 0xa02181a1, 0x60234363, 0x20230323, 0x4c0d4d41, + 0xc808c8c0, 0x9c1e8e92, 0x9c1c8c90, 0x383a0a32, 0x0c0c0c00, 0x2c2e0e22, 0xb83a8ab2, 0x6c2e4e62, + 0x9c1f8f93, 0x581a4a52, 0xf032c2f2, 0x90128292, 0xf033c3f3, 0x48094941, 0x78384870, 0xcc0cccc0, + 0x14150511, 0xf83bcbf3, 0x70304070, 0x74354571, 0x7c3f4f73, 0x34350531, 0x10100010, 0x00030303, + 0x64244460, 0x6c2d4d61, 0xc406c6c2, 0x74344470, 0xd415c5d1, 0xb43484b0, 0xe82acae2, 0x08090901, + 0x74364672, 0x18190911, 0xfc3ecef2, 0x40004040, 0x10120212, 0xe020c0e0, 0xbc3d8db1, 0x04050501, + 0xf83acaf2, 0x00010101, 0xf030c0f0, 0x282a0a22, 0x5c1e4e52, 0xa82989a1, 0x54164652, 0x40034343, + 0x84058581, 0x14140410, 0x88098981, 0x981b8b93, 0xb03080b0, 0xe425c5e1, 0x48084840, 0x78394971, + 0x94178793, 0xfc3cccf0, 0x1c1e0e12, 0x80028282, 0x20210121, 0x8c0c8c80, 0x181b0b13, 0x5c1f4f53, + 0x74374773, 0x54144450, 0xb03282b2, 0x1c1d0d11, 0x24250521, 0x4c0f4f43, 0x00000000, 0x44064642, + 0xec2dcde1, 0x58184850, 0x50124252, 0xe82bcbe3, 0x7c3e4e72, 0xd81acad2, 0xc809c9c1, 0xfc3dcdf1, + 0x30300030, 0x94158591, 0x64254561, 0x3c3c0c30, 0xb43686b2, 0xe424c4e0, 0xb83b8bb3, 0x7c3c4c70, + 0x0c0e0e02, 0x50104050, 0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393, + 0x34370733, 0xe427c7e3, 0x24240420, 0xa42484a0, 0xc80bcbc3, 0x50134353, 0x080a0a02, 0x84078783, + 0xd819c9d1, 0x4c0c4c40, 0x80038383, 0x8c0f8f83, 0xcc0ecec2, 0x383b0b33, 0x480a4a42, 0xb43787b3 + }; + + private static readonly uint[] SS2 = + { + + 0xa1a82989, 0x81840585, 0xd2d416c6, 0xd3d013c3, 0x50541444, 0x111c1d0d, 0xa0ac2c8c, 0x21242505, + 0x515c1d4d, 0x43400343, 0x10181808, 0x121c1e0e, 0x51501141, 0xf0fc3ccc, 0xc2c80aca, 0x63602343, + 0x20282808, 0x40440444, 0x20202000, 0x919c1d8d, 0xe0e020c0, 0xe2e022c2, 0xc0c808c8, 0x13141707, + 0xa1a42585, 0x838c0f8f, 0x03000303, 0x73783b4b, 0xb3b83b8b, 0x13101303, 0xd2d012c2, 0xe2ec2ece, + 0x70703040, 0x808c0c8c, 0x333c3f0f, 0xa0a82888, 0x32303202, 0xd1dc1dcd, 0xf2f436c6, 0x70743444, + 0xe0ec2ccc, 0x91941585, 0x03080b0b, 0x53541747, 0x505c1c4c, 0x53581b4b, 0xb1bc3d8d, 0x01000101, + 0x20242404, 0x101c1c0c, 0x73703343, 0x90981888, 0x10101000, 0xc0cc0ccc, 0xf2f032c2, 0xd1d819c9, + 0x202c2c0c, 0xe3e427c7, 0x72703242, 0x83800383, 0x93981b8b, 0xd1d011c1, 0x82840686, 0xc1c809c9, + 0x60602040, 0x50501040, 0xa3a02383, 0xe3e82bcb, 0x010c0d0d, 0xb2b43686, 0x929c1e8e, 0x434c0f4f, + 0xb3b43787, 0x52581a4a, 0xc2c406c6, 0x70783848, 0xa2a42686, 0x12101202, 0xa3ac2f8f, 0xd1d415c5, + 0x61602141, 0xc3c003c3, 0xb0b43484, 0x41400141, 0x52501242, 0x717c3d4d, 0x818c0d8d, 0x00080808, + 0x131c1f0f, 0x91981989, 0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xf3f437c7, 0xe1e021c1, + 0xf1fc3dcd, 0x72743646, 0x232c2f0f, 0x23242707, 0xb0b03080, 0x83880b8b, 0x020c0e0e, 0xa3a82b8b, + 0xa2a02282, 0x626c2e4e, 0x93901383, 0x414c0d4d, 0x61682949, 0x707c3c4c, 0x01080909, 0x02080a0a, + 0xb3bc3f8f, 0xe3ec2fcf, 0xf3f033c3, 0xc1c405c5, 0x83840787, 0x10141404, 0xf2fc3ece, 0x60642444, + 0xd2dc1ece, 0x222c2e0e, 0x43480b4b, 0x12181a0a, 0x02040606, 0x21202101, 0x63682b4b, 0x62642646, + 0x02000202, 0xf1f435c5, 0x92901282, 0x82880a8a, 0x000c0c0c, 0xb3b03383, 0x727c3e4e, 0xd0d010c0, + 0x72783a4a, 0x43440747, 0x92941686, 0xe1e425c5, 0x22242606, 0x80800080, 0xa1ac2d8d, 0xd3dc1fcf, + 0xa1a02181, 0x30303000, 0x33343707, 0xa2ac2e8e, 0x32343606, 0x11141505, 0x22202202, 0x30383808, + 0xf0f434c4, 0xa3a42787, 0x41440545, 0x404c0c4c, 0x81800181, 0xe1e829c9, 0x80840484, 0x93941787, + 0x31343505, 0xc3c80bcb, 0xc2cc0ece, 0x303c3c0c, 0x71703141, 0x11101101, 0xc3c407c7, 0x81880989, + 0x71743545, 0xf3f83bcb, 0xd2d81aca, 0xf0f838c8, 0x90941484, 0x51581949, 0x82800282, 0xc0c404c4, + 0xf3fc3fcf, 0x41480949, 0x31383909, 0x63642747, 0xc0c000c0, 0xc3cc0fcf, 0xd3d417c7, 0xb0b83888, + 0x030c0f0f, 0x828c0e8e, 0x42400242, 0x23202303, 0x91901181, 0x606c2c4c, 0xd3d81bcb, 0xa0a42484, + 0x30343404, 0xf1f031c1, 0x40480848, 0xc2c002c2, 0x636c2f4f, 0x313c3d0d, 0x212c2d0d, 0x40400040, + 0xb2bc3e8e, 0x323c3e0e, 0xb0bc3c8c, 0xc1c001c1, 0xa2a82a8a, 0xb2b83a8a, 0x424c0e4e, 0x51541545, + 0x33383b0b, 0xd0dc1ccc, 0x60682848, 0x737c3f4f, 0x909c1c8c, 0xd0d818c8, 0x42480a4a, 0x52541646, + 0x73743747, 0xa0a02080, 0xe1ec2dcd, 0x42440646, 0xb1b43585, 0x23282b0b, 0x61642545, 0xf2f83aca, + 0xe3e023c3, 0xb1b83989, 0xb1b03181, 0x939c1f8f, 0x525c1e4e, 0xf1f839c9, 0xe2e426c6, 0xb2b03282, + 0x31303101, 0xe2e82aca, 0x616c2d4d, 0x535c1f4f, 0xe0e424c4, 0xf0f030c0, 0xc1cc0dcd, 0x80880888, + 0x12141606, 0x32383a0a, 0x50581848, 0xd0d414c4, 0x62602242, 0x21282909, 0x03040707, 0x33303303, + 0xe0e828c8, 0x13181b0b, 0x01040505, 0x71783949, 0x90901080, 0x62682a4a, 0x22282a0a, 0x92981a8a + }; + + private static readonly uint[] SS3 = + { + + 0x08303838, 0xc8e0e828, 0x0d212c2d, 0x86a2a426, 0xcfc3cc0f, 0xced2dc1e, 0x83b3b033, 0x88b0b838, + 0x8fa3ac2f, 0x40606020, 0x45515415, 0xc7c3c407, 0x44404404, 0x4f636c2f, 0x4b63682b, 0x4b53581b, + 0xc3c3c003, 0x42626022, 0x03333033, 0x85b1b435, 0x09212829, 0x80a0a020, 0xc2e2e022, 0x87a3a427, + 0xc3d3d013, 0x81919011, 0x01111011, 0x06020406, 0x0c101c1c, 0x8cb0bc3c, 0x06323436, 0x4b43480b, + 0xcfe3ec2f, 0x88808808, 0x4c606c2c, 0x88a0a828, 0x07131417, 0xc4c0c404, 0x06121416, 0xc4f0f434, + 0xc2c2c002, 0x45414405, 0xc1e1e021, 0xc6d2d416, 0x0f333c3f, 0x0d313c3d, 0x8e828c0e, 0x88909818, + 0x08202828, 0x4e424c0e, 0xc6f2f436, 0x0e323c3e, 0x85a1a425, 0xc9f1f839, 0x0d010c0d, 0xcfd3dc1f, + 0xc8d0d818, 0x0b23282b, 0x46626426, 0x4a72783a, 0x07232427, 0x0f232c2f, 0xc1f1f031, 0x42727032, + 0x42424002, 0xc4d0d414, 0x41414001, 0xc0c0c000, 0x43737033, 0x47636427, 0x8ca0ac2c, 0x8b83880b, + 0xc7f3f437, 0x8da1ac2d, 0x80808000, 0x0f131c1f, 0xcac2c80a, 0x0c202c2c, 0x8aa2a82a, 0x04303434, + 0xc2d2d012, 0x0b03080b, 0xcee2ec2e, 0xc9e1e829, 0x4d515c1d, 0x84909414, 0x08101818, 0xc8f0f838, + 0x47535417, 0x8ea2ac2e, 0x08000808, 0xc5c1c405, 0x03131013, 0xcdc1cc0d, 0x86828406, 0x89b1b839, + 0xcff3fc3f, 0x4d717c3d, 0xc1c1c001, 0x01313031, 0xc5f1f435, 0x8a82880a, 0x4a62682a, 0x81b1b031, + 0xc1d1d011, 0x00202020, 0xc7d3d417, 0x02020002, 0x02222022, 0x04000404, 0x48606828, 0x41717031, + 0x07030407, 0xcbd3d81b, 0x8d919c1d, 0x89919819, 0x41616021, 0x8eb2bc3e, 0xc6e2e426, 0x49515819, + 0xcdd1dc1d, 0x41515011, 0x80909010, 0xccd0dc1c, 0x8a92981a, 0x83a3a023, 0x8ba3a82b, 0xc0d0d010, + 0x81818001, 0x0f030c0f, 0x47434407, 0x0a12181a, 0xc3e3e023, 0xcce0ec2c, 0x8d818c0d, 0x8fb3bc3f, + 0x86929416, 0x4b73783b, 0x4c505c1c, 0x82a2a022, 0x81a1a021, 0x43636023, 0x03232023, 0x4d414c0d, + 0xc8c0c808, 0x8e929c1e, 0x8c909c1c, 0x0a32383a, 0x0c000c0c, 0x0e222c2e, 0x8ab2b83a, 0x4e626c2e, + 0x8f939c1f, 0x4a52581a, 0xc2f2f032, 0x82929012, 0xc3f3f033, 0x49414809, 0x48707838, 0xccc0cc0c, + 0x05111415, 0xcbf3f83b, 0x40707030, 0x45717435, 0x4f737c3f, 0x05313435, 0x00101010, 0x03030003, + 0x44606424, 0x4d616c2d, 0xc6c2c406, 0x44707434, 0xc5d1d415, 0x84b0b434, 0xcae2e82a, 0x09010809, + 0x46727436, 0x09111819, 0xcef2fc3e, 0x40404000, 0x02121012, 0xc0e0e020, 0x8db1bc3d, 0x05010405, + 0xcaf2f83a, 0x01010001, 0xc0f0f030, 0x0a22282a, 0x4e525c1e, 0x89a1a829, 0x46525416, 0x43434003, + 0x85818405, 0x04101414, 0x89818809, 0x8b93981b, 0x80b0b030, 0xc5e1e425, 0x48404808, 0x49717839, + 0x87939417, 0xccf0fc3c, 0x0e121c1e, 0x82828002, 0x01212021, 0x8c808c0c, 0x0b13181b, 0x4f535c1f, + 0x47737437, 0x44505414, 0x82b2b032, 0x0d111c1d, 0x05212425, 0x4f434c0f, 0x00000000, 0x46424406, + 0xcde1ec2d, 0x48505818, 0x42525012, 0xcbe3e82b, 0x4e727c3e, 0xcad2d81a, 0xc9c1c809, 0xcdf1fc3d, + 0x00303030, 0x85919415, 0x45616425, 0x0c303c3c, 0x86b2b436, 0xc4e0e424, 0x8bb3b83b, 0x4c707c3c, + 0x0e020c0e, 0x40505010, 0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013, + 0x07333437, 0xc7e3e427, 0x04202424, 0x84a0a424, 0xcbc3c80b, 0x43535013, 0x0a02080a, 0x87838407, + 0xc9d1d819, 0x4c404c0c, 0x83838003, 0x8f838c0f, 0xcec2cc0e, 0x0b33383b, 0x4a42480a, 0x87b3b437 + }; + + private static readonly uint[] KC = + { + 0x9e3779b9, 0x3c6ef373, 0x78dde6e6, 0xf1bbcdcc, + 0xe3779b99, 0xc6ef3733, 0x8dde6e67, 0x1bbcdccf, + 0x3779b99e, 0x6ef3733c, 0xdde6e678, 0xbbcdccf1, + 0x779b99e3, 0xef3733c6, 0xde6e678d, 0xbcdccf1b + }; + + private int[] wKey; + private bool forEncryption; + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + wKey = createWorkingKey(((KeyParameter)parameters).GetKey()); + } + + public string AlgorithmName + { + get { return "SEED"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BlockSize; + } + + public int ProcessBlock( + byte[] inBuf, + int inOff, + byte[] outBuf, + int outOff) + { + if (wKey == null) + throw new InvalidOperationException("SEED engine not initialised"); + if (inOff + BlockSize > inBuf.Length) + throw new DataLengthException("input buffer too short"); + if (outOff + BlockSize > outBuf.Length) + throw new DataLengthException("output buffer too short"); + + long l = bytesToLong(inBuf, inOff + 0); + long r = bytesToLong(inBuf, inOff + 8); + + if (forEncryption) + { + for (int i = 0; i < 16; i++) + { + long nl = r; + + r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r); + l = nl; + } + } + else + { + for (int i = 15; i >= 0; i--) + { + long nl = r; + + r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r); + l = nl; + } + } + + longToBytes(outBuf, outOff + 0, r); + longToBytes(outBuf, outOff + 8, l); + + return BlockSize; + } + + public void Reset() + { + } + + private int[] createWorkingKey( + byte[] inKey) + { + int[] key = new int[32]; + long lower = bytesToLong(inKey, 0); + long upper = bytesToLong(inKey, 8); + + int key0 = extractW0(lower); + int key1 = extractW1(lower); + int key2 = extractW0(upper); + int key3 = extractW1(upper); + + for (int i = 0; i < 16; i++) + { + key[2 * i] = G(key0 + key2 - (int)KC[i]); + key[2 * i + 1] = G(key1 - key3 + (int)KC[i]); + + if (i % 2 == 0) + { + lower = rotateRight8(lower); + key0 = extractW0(lower); + key1 = extractW1(lower); + } + else + { + upper = rotateLeft8(upper); + key2 = extractW0(upper); + key3 = extractW1(upper); + } + } + + return key; + } + + private int extractW1( + long lVal) + { + return (int)lVal; + } + + private int extractW0( + long lVal) + { + return (int)(lVal >> 32); + } + + private long rotateLeft8( + long x) + { + return (x << 8) | ((long)((ulong) x >> 56)); + } + + private long rotateRight8( + long x) + { + return ((long)((ulong) x >> 8)) | (x << 56); + } + + private long bytesToLong( + byte[] src, + int srcOff) + { + long word = 0; + + for (int i = 0; i <= 7; i++) + { + word = (word << 8) + (src[i + srcOff] & 0xff); + } + + return word; + } + + private void longToBytes( + byte[] dest, + int destOff, + long value) + { + for (int i = 0; i < 8; i++) + { + dest[i + destOff] = (byte)(value >> ((7 - i) * 8)); + } + } + + private int G( + int x) + { + return (int)(SS0[x & 0xff] ^ SS1[(x >> 8) & 0xff] ^ SS2[(x >> 16) & 0xff] ^ SS3[(x >> 24) & 0xff]); + } + + private long F( + int ki0, + int ki1, + long r) + { + int r0 = (int)(r >> 32); + int r1 = (int)r; + int rd1 = phaseCalc2(r0, ki0, r1, ki1); + int rd0 = rd1 + phaseCalc1(r0, ki0, r1, ki1); + + return ((long)rd0 << 32) | (rd1 & 0xffffffffL); + } + + private int phaseCalc1( + int r0, + int ki0, + int r1, + int ki1) + { + return G(G((r0 ^ ki0) ^ (r1 ^ ki1)) + (r0 ^ ki0)); + } + + private int phaseCalc2( + int r0, + int ki0, + int r1, + int ki1) + { + return G(phaseCalc1(r0, ki0, r1, ki1) + G((r0 ^ ki0) ^ (r1 ^ ki1))); + } + } +} diff --git a/src/core/srcbc/crypto/engines/SEEDWrapEngine.cs b/src/core/srcbc/crypto/engines/SEEDWrapEngine.cs new file mode 100644 index 0000000..7b2ca9f --- /dev/null +++ b/src/core/srcbc/crypto/engines/SEEDWrapEngine.cs @@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Engines +{ + /// + /// An implementation of the SEED key wrapper based on RFC 4010/RFC 3394. + ///

+ /// For further details see: http://www.ietf.org/rfc/rfc4010.txt. + /// + public class SeedWrapEngine + : Rfc3394WrapEngine + { + public SeedWrapEngine() + : base(new SeedEngine()) + { + } + } +} diff --git a/src/core/srcbc/crypto/engines/Salsa20Engine.cs b/src/core/srcbc/crypto/engines/Salsa20Engine.cs new file mode 100644 index 0000000..c4ea809 --- /dev/null +++ b/src/core/srcbc/crypto/engines/Salsa20Engine.cs @@ -0,0 +1,363 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 + */ + public class Salsa20Engine + : IStreamCipher + { + /** Constants */ + private const int stateSize = 16; // 16, 32 bit ints = 64 bytes + + private readonly static byte[] + sigma = Encoding.ASCII.GetBytes("expand 32-byte k"), + tau = Encoding.ASCII.GetBytes("expand 16-byte k"); + + /* + * variables to hold the state of the engine + * during encryption and decryption + */ + private int index = 0; + private int[] engineState = new int[stateSize]; // state + private int[] x = new int[stateSize] ; // internal buffer + private byte[] keyStream = new byte[stateSize * 4], // expanded state, 64 bytes + workingKey = null, + workingIV = null; + private bool initialised = false; + + /* + * internal counter + */ + private int cW0, cW1, cW2; + + /** + * initialise a Salsa20 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + /* + * Salsa20 encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. (Like 90% of stream ciphers) + */ + + ParametersWithIV ivParams = parameters as ParametersWithIV; + + if (ivParams == null) + throw new ArgumentException("Salsa20 Init requires an IV", "parameters"); + + byte[] iv = ivParams.GetIV(); + + if (iv == null || iv.Length != 8) + throw new ArgumentException("Salsa20 requires exactly 8 bytes of IV"); + + KeyParameter key = ivParams.Parameters as KeyParameter; + + if (key == null) + throw new ArgumentException("Salsa20 Init requires a key", "parameters"); + + workingKey = key.GetKey(); + workingIV = iv; + + setKey(workingKey, workingIV); + } + + public string AlgorithmName + { + get { return "Salsa20"; } + } + + public byte ReturnByte( + byte input) + { + if (limitExceeded()) + { + throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV"); + } + + if (index == 0) + { + salsa20WordToByte(engineState, keyStream); + engineState[8]++; + if (engineState[8] == 0) + { + engineState[9]++; + } + } + byte output = (byte)(keyStream[index]^input); + index = (index + 1) & 63; + + return output; + } + + public void ProcessBytes( + byte[] inBytes, + int inOff, + int len, + byte[] outBytes, + int outOff) + { + if (!initialised) + { + throw new InvalidOperationException(AlgorithmName + " not initialised"); + } + + if ((inOff + len) > inBytes.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (limitExceeded(len)) + { + throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV"); + } + + for (int i = 0; i < len; i++) + { + if (index == 0) + { + salsa20WordToByte(engineState, keyStream); + engineState[8]++; + if (engineState[8] == 0) + { + engineState[9]++; + } + } + outBytes[i+outOff] = (byte)(keyStream[index]^inBytes[i+inOff]); + index = (index + 1) & 63; + } + } + + public void Reset() + { + setKey(workingKey, workingIV); + } + + // Private implementation + + private void setKey(byte[] keyBytes, byte[] ivBytes) + { + workingKey = keyBytes; + workingIV = ivBytes; + + index = 0; + resetCounter(); + int offset = 0; + byte[] constants; + + // Key + engineState[1] = byteToIntLittle(workingKey, 0); + engineState[2] = byteToIntLittle(workingKey, 4); + engineState[3] = byteToIntLittle(workingKey, 8); + engineState[4] = byteToIntLittle(workingKey, 12); + + if (workingKey.Length == 32) + { + constants = sigma; + offset = 16; + } + else + { + constants = tau; + } + + engineState[11] = byteToIntLittle(workingKey, offset); + engineState[12] = byteToIntLittle(workingKey, offset+4); + engineState[13] = byteToIntLittle(workingKey, offset+8); + engineState[14] = byteToIntLittle(workingKey, offset+12); + engineState[0 ] = byteToIntLittle(constants, 0); + engineState[5 ] = byteToIntLittle(constants, 4); + engineState[10] = byteToIntLittle(constants, 8); + engineState[15] = byteToIntLittle(constants, 12); + + // IV + engineState[6] = byteToIntLittle(workingIV, 0); + engineState[7] = byteToIntLittle(workingIV, 4); + engineState[8] = engineState[9] = 0; + + initialised = true; + } + + /** + * Salsa20 function + * + * @param input input data + * + * @return keystream + */ + private void salsa20WordToByte( + int[] input, + byte[] output) + { + Array.Copy(input, 0, x, 0, input.Length); + + for (int i = 0; i < 10; i++) + { + x[ 4] ^= rotl((x[ 0]+x[12]), 7); + x[ 8] ^= rotl((x[ 4]+x[ 0]), 9); + x[12] ^= rotl((x[ 8]+x[ 4]),13); + x[ 0] ^= rotl((x[12]+x[ 8]),18); + x[ 9] ^= rotl((x[ 5]+x[ 1]), 7); + x[13] ^= rotl((x[ 9]+x[ 5]), 9); + x[ 1] ^= rotl((x[13]+x[ 9]),13); + x[ 5] ^= rotl((x[ 1]+x[13]),18); + x[14] ^= rotl((x[10]+x[ 6]), 7); + x[ 2] ^= rotl((x[14]+x[10]), 9); + x[ 6] ^= rotl((x[ 2]+x[14]),13); + x[10] ^= rotl((x[ 6]+x[ 2]),18); + x[ 3] ^= rotl((x[15]+x[11]), 7); + x[ 7] ^= rotl((x[ 3]+x[15]), 9); + x[11] ^= rotl((x[ 7]+x[ 3]),13); + x[15] ^= rotl((x[11]+x[ 7]),18); + x[ 1] ^= rotl((x[ 0]+x[ 3]), 7); + x[ 2] ^= rotl((x[ 1]+x[ 0]), 9); + x[ 3] ^= rotl((x[ 2]+x[ 1]),13); + x[ 0] ^= rotl((x[ 3]+x[ 2]),18); + x[ 6] ^= rotl((x[ 5]+x[ 4]), 7); + x[ 7] ^= rotl((x[ 6]+x[ 5]), 9); + x[ 4] ^= rotl((x[ 7]+x[ 6]),13); + x[ 5] ^= rotl((x[ 4]+x[ 7]),18); + x[11] ^= rotl((x[10]+x[ 9]), 7); + x[ 8] ^= rotl((x[11]+x[10]), 9); + x[ 9] ^= rotl((x[ 8]+x[11]),13); + x[10] ^= rotl((x[ 9]+x[ 8]),18); + x[12] ^= rotl((x[15]+x[14]), 7); + x[13] ^= rotl((x[12]+x[15]), 9); + x[14] ^= rotl((x[13]+x[12]),13); + x[15] ^= rotl((x[14]+x[13]),18); + } + + int offset = 0; + for (int i = 0; i < stateSize; i++) + { + intToByteLittle(x[i] + input[i], output, offset); + offset += 4; + } + + for (int i = stateSize; i < x.Length; i++) + { + intToByteLittle(x[i], output, offset); + offset += 4; + } + } + + /** + * 32 bit word to 4 byte array in little endian order + * + * @param x value to 'unpack' + * + * @return value of x expressed as a byte[] array in little endian order + */ + private byte[] intToByteLittle( + int x, + byte[] bs, + int off) + { + bs[off] = (byte)x; + bs[off + 1] = (byte)(x >> 8); + bs[off + 2] = (byte)(x >> 16); + bs[off + 3] = (byte)(x >> 24); + return bs; + } + + /** + * Rotate left + * + * @param x value to rotate + * @param y amount to rotate x + * + * @return rotated x + */ + private int rotl( + int x, + int y) + { + return (x << y) | ((int)((uint) x >> -y)); + } + + /** + * Pack byte[] array into an int in little endian order + * + * @param x byte array to 'pack' + * @param offset only x[offset]..x[offset+3] will be packed + * + * @return x[offset]..x[offset+3] 'packed' into an int in little-endian order + */ + private int byteToIntLittle( + byte[] x, + int offset) + { + return ((x[offset] & 255)) | + ((x[offset + 1] & 255) << 8) | + ((x[offset + 2] & 255) << 16) | + (x[offset + 3] << 24); + } + + private void resetCounter() + { + cW0 = 0; + cW1 = 0; + cW2 = 0; + } + + private bool limitExceeded() + { + cW0++; + if (cW0 == 0) + { + cW1++; + if (cW1 == 0) + { + cW2++; + return (cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + + return false; + } + + /* + * this relies on the fact len will always be positive. + */ + private bool limitExceeded( + int len) + { + if (cW0 >= 0) + { + cW0 += len; + } + else + { + cW0 += len; + if (cW0 >= 0) + { + cW1++; + if (cW1 == 0) + { + cW2++; + return (cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + } + + return false; + } + } +} diff --git a/src/core/srcbc/crypto/engines/SerpentEngine.cs b/src/core/srcbc/crypto/engines/SerpentEngine.cs new file mode 100644 index 0000000..5b63b38 --- /dev/null +++ b/src/core/srcbc/crypto/engines/SerpentEngine.cs @@ -0,0 +1,779 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Serpent is a 128-bit 32-round block cipher with variable key lengths, + * including 128, 192 and 256 bit keys conjectured to be at least as + * secure as three-key triple-DES. + *

+ * Serpent was designed by Ross Anderson, Eli Biham and Lars Knudsen as a + * candidate algorithm for the NIST AES Quest.> + *

+ *

+ * For full details see the The Serpent home page + *

+ */ + public class SerpentEngine + : IBlockCipher + { + private const int BLOCK_SIZE = 16; + + static readonly int ROUNDS = 32; + static readonly int PHI = unchecked((int)0x9E3779B9); // (Sqrt(5) - 1) * 2**31 + + private bool encrypting; + private int[] wKey; + + private int X0, X1, X2, X3; // registers + + /** + * initialise a Serpent cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to Serpent init - " + parameters.GetType().ToString()); + + this.encrypting = forEncryption; + this.wKey = MakeWorkingKey(((KeyParameter)parameters).GetKey()); + } + + public string AlgorithmName + { + get { return "Serpent"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (wKey == null) + throw new InvalidOperationException("Serpent not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, outOff); + } + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + /** + * Expand a user-supplied key material into a session key. + * + * @param key The user-key bytes (multiples of 4) to use. + * @exception ArgumentException + */ + private int[] MakeWorkingKey( + byte[] key) + { + // + // pad key to 256 bits + // + int[] kPad = new int[16]; + int off = 0; + int length = 0; + + for (off = key.Length - 4; off > 0; off -= 4) + { + kPad[length++] = BytesToWord(key, off); + } + + if (off == 0) + { + kPad[length++] = BytesToWord(key, 0); + if (length < 8) + { + kPad[length] = 1; + } + } + else + { + throw new ArgumentException("key must be a multiple of 4 bytes"); + } + + // + // expand the padded key up to 33 x 128 bits of key material + // + int amount = (ROUNDS + 1) * 4; + int[] w = new int[amount]; + + // + // compute w0 to w7 from w-8 to w-1 + // + for (int i = 8; i < 16; i++) + { + kPad[i] = RotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11); + } + + Array.Copy(kPad, 8, w, 0, 8); + + // + // compute w8 to w136 + // + for (int i = 8; i < amount; i++) + { + w[i] = RotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11); + } + + // + // create the working keys by processing w with the Sbox and IP + // + Sb3(w[0], w[1], w[2], w[3]); + w[0] = X0; w[1] = X1; w[2] = X2; w[3] = X3; + Sb2(w[4], w[5], w[6], w[7]); + w[4] = X0; w[5] = X1; w[6] = X2; w[7] = X3; + Sb1(w[8], w[9], w[10], w[11]); + w[8] = X0; w[9] = X1; w[10] = X2; w[11] = X3; + Sb0(w[12], w[13], w[14], w[15]); + w[12] = X0; w[13] = X1; w[14] = X2; w[15] = X3; + Sb7(w[16], w[17], w[18], w[19]); + w[16] = X0; w[17] = X1; w[18] = X2; w[19] = X3; + Sb6(w[20], w[21], w[22], w[23]); + w[20] = X0; w[21] = X1; w[22] = X2; w[23] = X3; + Sb5(w[24], w[25], w[26], w[27]); + w[24] = X0; w[25] = X1; w[26] = X2; w[27] = X3; + Sb4(w[28], w[29], w[30], w[31]); + w[28] = X0; w[29] = X1; w[30] = X2; w[31] = X3; + Sb3(w[32], w[33], w[34], w[35]); + w[32] = X0; w[33] = X1; w[34] = X2; w[35] = X3; + Sb2(w[36], w[37], w[38], w[39]); + w[36] = X0; w[37] = X1; w[38] = X2; w[39] = X3; + Sb1(w[40], w[41], w[42], w[43]); + w[40] = X0; w[41] = X1; w[42] = X2; w[43] = X3; + Sb0(w[44], w[45], w[46], w[47]); + w[44] = X0; w[45] = X1; w[46] = X2; w[47] = X3; + Sb7(w[48], w[49], w[50], w[51]); + w[48] = X0; w[49] = X1; w[50] = X2; w[51] = X3; + Sb6(w[52], w[53], w[54], w[55]); + w[52] = X0; w[53] = X1; w[54] = X2; w[55] = X3; + Sb5(w[56], w[57], w[58], w[59]); + w[56] = X0; w[57] = X1; w[58] = X2; w[59] = X3; + Sb4(w[60], w[61], w[62], w[63]); + w[60] = X0; w[61] = X1; w[62] = X2; w[63] = X3; + Sb3(w[64], w[65], w[66], w[67]); + w[64] = X0; w[65] = X1; w[66] = X2; w[67] = X3; + Sb2(w[68], w[69], w[70], w[71]); + w[68] = X0; w[69] = X1; w[70] = X2; w[71] = X3; + Sb1(w[72], w[73], w[74], w[75]); + w[72] = X0; w[73] = X1; w[74] = X2; w[75] = X3; + Sb0(w[76], w[77], w[78], w[79]); + w[76] = X0; w[77] = X1; w[78] = X2; w[79] = X3; + Sb7(w[80], w[81], w[82], w[83]); + w[80] = X0; w[81] = X1; w[82] = X2; w[83] = X3; + Sb6(w[84], w[85], w[86], w[87]); + w[84] = X0; w[85] = X1; w[86] = X2; w[87] = X3; + Sb5(w[88], w[89], w[90], w[91]); + w[88] = X0; w[89] = X1; w[90] = X2; w[91] = X3; + Sb4(w[92], w[93], w[94], w[95]); + w[92] = X0; w[93] = X1; w[94] = X2; w[95] = X3; + Sb3(w[96], w[97], w[98], w[99]); + w[96] = X0; w[97] = X1; w[98] = X2; w[99] = X3; + Sb2(w[100], w[101], w[102], w[103]); + w[100] = X0; w[101] = X1; w[102] = X2; w[103] = X3; + Sb1(w[104], w[105], w[106], w[107]); + w[104] = X0; w[105] = X1; w[106] = X2; w[107] = X3; + Sb0(w[108], w[109], w[110], w[111]); + w[108] = X0; w[109] = X1; w[110] = X2; w[111] = X3; + Sb7(w[112], w[113], w[114], w[115]); + w[112] = X0; w[113] = X1; w[114] = X2; w[115] = X3; + Sb6(w[116], w[117], w[118], w[119]); + w[116] = X0; w[117] = X1; w[118] = X2; w[119] = X3; + Sb5(w[120], w[121], w[122], w[123]); + w[120] = X0; w[121] = X1; w[122] = X2; w[123] = X3; + Sb4(w[124], w[125], w[126], w[127]); + w[124] = X0; w[125] = X1; w[126] = X2; w[127] = X3; + Sb3(w[128], w[129], w[130], w[131]); + w[128] = X0; w[129] = X1; w[130] = X2; w[131] = X3; + + return w; + } + + private int RotateLeft( + int x, + int bits) + { + return ((x << bits) | (int) ((uint)x >> (32 - bits))); + } + + private int RotateRight( + int x, + int bits) + { + return ( (int)((uint)x >> bits) | (x << (32 - bits))); + } + + private int BytesToWord( + byte[] src, + int srcOff) + { + return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) | + ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff))); + } + + private void WordToBytes( + int word, + byte[] dst, + int dstOff) + { + dst[dstOff + 3] = (byte)(word); + dst[dstOff + 2] = (byte)((uint)word >> 8); + dst[dstOff + 1] = (byte)((uint)word >> 16); + dst[dstOff] = (byte)((uint)word >> 24); + } + + /** + * Encrypt one block of plaintext. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + */ + private void EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + X3 = BytesToWord(input, inOff); + X2 = BytesToWord(input, inOff + 4); + X1 = BytesToWord(input, inOff + 8); + X0 = BytesToWord(input, inOff + 12); + + Sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT(); + Sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT(); + Sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT(); + Sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT(); + Sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT(); + Sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT(); + Sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT(); + Sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT(); + Sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT(); + Sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT(); + Sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT(); + Sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT(); + Sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT(); + Sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT(); + Sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT(); + Sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT(); + Sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT(); + Sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT(); + Sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT(); + Sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT(); + Sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT(); + Sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT(); + Sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT(); + Sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT(); + Sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT(); + Sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT(); + Sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT(); + Sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT(); + Sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT(); + Sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT(); + Sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT(); + Sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3); + + WordToBytes(wKey[131] ^ X3, outBytes, outOff); + WordToBytes(wKey[130] ^ X2, outBytes, outOff + 4); + WordToBytes(wKey[129] ^ X1, outBytes, outOff + 8); + WordToBytes(wKey[128] ^ X0, outBytes, outOff + 12); + } + + /** + * Decrypt one block of ciphertext. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + */ + private void DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + X3 = wKey[131] ^ BytesToWord(input, inOff); + X2 = wKey[130] ^ BytesToWord(input, inOff + 4); + X1 = wKey[129] ^ BytesToWord(input, inOff + 8); + X0 = wKey[128] ^ BytesToWord(input, inOff + 12); + + Ib7(X0, X1, X2, X3); + X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39]; + InverseLT(); Ib0(X0, X1, X2, X3); + X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35]; + InverseLT(); Ib7(X0, X1, X2, X3); + X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31]; + InverseLT(); Ib6(X0, X1, X2, X3); + X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27]; + InverseLT(); Ib5(X0, X1, X2, X3); + X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23]; + InverseLT(); Ib4(X0, X1, X2, X3); + X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19]; + InverseLT(); Ib3(X0, X1, X2, X3); + X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15]; + InverseLT(); Ib2(X0, X1, X2, X3); + X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11]; + InverseLT(); Ib1(X0, X1, X2, X3); + X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7]; + InverseLT(); Ib0(X0, X1, X2, X3); + + WordToBytes(X3 ^ wKey[3], outBytes, outOff); + WordToBytes(X2 ^ wKey[2], outBytes, outOff + 4); + WordToBytes(X1 ^ wKey[1], outBytes, outOff + 8); + WordToBytes(X0 ^ wKey[0], outBytes, outOff + 12); + } + + /* + * The sboxes below are based on the work of Brian Gladman and + * Sam Simpson, whose original notice appears below. + *

+ * For further details see: + * http://fp.gladman.plus.com/cryptography_technology/serpent/ + *

+ */ + + /* Partially optimised Serpent S Box bool functions derived */ + /* using a recursive descent analyser but without a full search */ + /* of all subtrees. This set of S boxes is the result of work */ + /* by Sam Simpson and Brian Gladman using the spare time on a */ + /* cluster of high capacity servers to search for S boxes with */ + /* this customised search engine. There are now an average of */ + /* 15.375 terms per S box. */ + /* */ + /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */ + /* and Sam Simpson (s.simpson@mia.co.uk) */ + /* 17th December 1998 */ + /* */ + /* We hereby give permission for information in this file to be */ + /* used freely subject only to acknowledgement of its origin. */ + + /** + * S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms. + */ + private void Sb0(int a, int b, int c, int d) + { + int t1 = a ^ d; + int t3 = c ^ t1; + int t4 = b ^ t3; + X3 = (a & d) ^ t4; + int t7 = a ^ (b & t1); + X2 = t4 ^ (c | t7); + int t12 = X3 & (t3 ^ t7); + X1 = (~t3) ^ t12; + X0 = t12 ^ (~t7); + } + + /** + * InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms. + */ + private void Ib0(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = a ^ b; + int t4 = d ^ (t1 | t2); + int t5 = c ^ t4; + X2 = t2 ^ t5; + int t8 = t1 ^ (d & t2); + X1 = t4 ^ (X2 & t8); + X3 = (a & t4) ^ (t5 | X1); + X0 = X3 ^ (t5 ^ t8); + } + + /** + * S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms. + */ + private void Sb1(int a, int b, int c, int d) + { + int t2 = b ^ (~a); + int t5 = c ^ (a | t2); + X2 = d ^ t5; + int t7 = b ^ (d | t2); + int t8 = t2 ^ X2; + X3 = t8 ^ (t5 & t7); + int t11 = t5 ^ t7; + X1 = X3 ^ t11; + X0 = t5 ^ (t8 & t11); + } + + /** + * InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps. + */ + private void Ib1(int a, int b, int c, int d) + { + int t1 = b ^ d; + int t3 = a ^ (b & t1); + int t4 = t1 ^ t3; + X3 = c ^ t4; + int t7 = b ^ (t1 & t3); + int t8 = X3 | t7; + X1 = t3 ^ t8; + int t10 = ~X1; + int t11 = X3 ^ t7; + X0 = t10 ^ t11; + X2 = t4 ^ (t10 | t11); + } + + /** + * S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms. + */ + private void Sb2(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = b ^ d; + int t3 = c & t1; + X0 = t2 ^ t3; + int t5 = c ^ t1; + int t6 = c ^ X0; + int t7 = b & t6; + X3 = t5 ^ t7; + X2 = a ^ ((d | t7) & (X0 | t5)); + X1 = (t2 ^ X3) ^ (X2 ^ (d | t1)); + } + + /** + * InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps. + */ + private void Ib2(int a, int b, int c, int d) + { + int t1 = b ^ d; + int t2 = ~t1; + int t3 = a ^ c; + int t4 = c ^ t1; + int t5 = b & t4; + X0 = t3 ^ t5; + int t7 = a | t2; + int t8 = d ^ t7; + int t9 = t3 | t8; + X3 = t1 ^ t9; + int t11 = ~t4; + int t12 = X0 | X3; + X1 = t11 ^ t12; + X2 = (d & t11) ^ (t3 ^ t12); + } + + /** + * S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms. + */ + private void Sb3(int a, int b, int c, int d) + { + int t1 = a ^ b; + int t2 = a & c; + int t3 = a | d; + int t4 = c ^ d; + int t5 = t1 & t3; + int t6 = t2 | t5; + X2 = t4 ^ t6; + int t8 = b ^ t3; + int t9 = t6 ^ t8; + int t10 = t4 & t9; + X0 = t1 ^ t10; + int t12 = X2 & X0; + X1 = t9 ^ t12; + X3 = (b | d) ^ (t4 ^ t12); + } + + /** + * InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms + */ + private void Ib3(int a, int b, int c, int d) + { + int t1 = a | b; + int t2 = b ^ c; + int t3 = b & t2; + int t4 = a ^ t3; + int t5 = c ^ t4; + int t6 = d | t4; + X0 = t2 ^ t6; + int t8 = t2 | t6; + int t9 = d ^ t8; + X2 = t5 ^ t9; + int t11 = t1 ^ t9; + int t12 = X0 & t11; + X3 = t4 ^ t12; + X1 = X3 ^ (X0 ^ t11); + } + + /** + * S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms. + */ + private void Sb4(int a, int b, int c, int d) + { + int t1 = a ^ d; + int t2 = d & t1; + int t3 = c ^ t2; + int t4 = b | t3; + X3 = t1 ^ t4; + int t6 = ~b; + int t7 = t1 | t6; + X0 = t3 ^ t7; + int t9 = a & X0; + int t10 = t1 ^ t6; + int t11 = t4 & t10; + X2 = t9 ^ t11; + X1 = (a ^ t3) ^ (t10 & X2); + } + + /** + * InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms. + */ + private void Ib4(int a, int b, int c, int d) + { + int t1 = c | d; + int t2 = a & t1; + int t3 = b ^ t2; + int t4 = a & t3; + int t5 = c ^ t4; + X1 = d ^ t5; + int t7 = ~a; + int t8 = t5 & X1; + X3 = t3 ^ t8; + int t10 = X1 | t7; + int t11 = d ^ t10; + X0 = X3 ^ t11; + X2 = (t3 & t11) ^ (X1 ^ t7); + } + + /** + * S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms. + */ + private void Sb5(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = a ^ b; + int t3 = a ^ d; + int t4 = c ^ t1; + int t5 = t2 | t3; + X0 = t4 ^ t5; + int t7 = d & X0; + int t8 = t2 ^ X0; + X1 = t7 ^ t8; + int t10 = t1 | X0; + int t11 = t2 | t7; + int t12 = t3 ^ t10; + X2 = t11 ^ t12; + X3 = (b ^ t7) ^ (X1 & t12); + } + + /** + * InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms. + */ + private void Ib5(int a, int b, int c, int d) + { + int t1 = ~c; + int t2 = b & t1; + int t3 = d ^ t2; + int t4 = a & t3; + int t5 = b ^ t1; + X3 = t4 ^ t5; + int t7 = b | X3; + int t8 = a & t7; + X1 = t3 ^ t8; + int t10 = a | d; + int t11 = t1 ^ t7; + X0 = t10 ^ t11; + X2 = (b & t10) ^ (t4 | (a ^ c)); + } + + /** + * S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms. + */ + private void Sb6(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = a ^ d; + int t3 = b ^ t2; + int t4 = t1 | t2; + int t5 = c ^ t4; + X1 = b ^ t5; + int t7 = t2 | X1; + int t8 = d ^ t7; + int t9 = t5 & t8; + X2 = t3 ^ t9; + int t11 = t5 ^ t8; + X0 = X2 ^ t11; + X3 = (~t5) ^ (t3 & t11); + } + + /** + * InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms. + */ + private void Ib6(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = a ^ b; + int t3 = c ^ t2; + int t4 = c | t1; + int t5 = d ^ t4; + X1 = t3 ^ t5; + int t7 = t3 & t5; + int t8 = t2 ^ t7; + int t9 = b | t8; + X3 = t5 ^ t9; + int t11 = b | X3; + X0 = t8 ^ t11; + X2 = (d & t1) ^ (t3 ^ t11); + } + + /** + * S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms. + */ + private void Sb7(int a, int b, int c, int d) + { + int t1 = b ^ c; + int t2 = c & t1; + int t3 = d ^ t2; + int t4 = a ^ t3; + int t5 = d | t1; + int t6 = t4 & t5; + X1 = b ^ t6; + int t8 = t3 | X1; + int t9 = a & t4; + X3 = t1 ^ t9; + int t11 = t4 ^ t8; + int t12 = X3 & t11; + X2 = t3 ^ t12; + X0 = (~t11) ^ (X3 & X2); + } + + /** + * InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms. + */ + private void Ib7(int a, int b, int c, int d) + { + int t3 = c | (a & b); + int t4 = d & (a | b); + X3 = t3 ^ t4; + int t6 = ~d; + int t7 = b ^ t4; + int t9 = t7 | (X3 ^ t6); + X1 = a ^ t9; + X0 = (c ^ t7) ^ (d | X1); + X2 = (t3 ^ X1) ^ (X0 ^ (a & X3)); + } + + /** + * Apply the linear transformation to the register set. + */ + private void LT() + { + int x0 = RotateLeft(X0, 13); + int x2 = RotateLeft(X2, 3); + int x1 = X1 ^ x0 ^ x2 ; + int x3 = X3 ^ x2 ^ x0 << 3; + + X1 = RotateLeft(x1, 1); + X3 = RotateLeft(x3, 7); + X0 = RotateLeft(x0 ^ X1 ^ X3, 5); + X2 = RotateLeft(x2 ^ X3 ^ (X1 << 7), 22); + } + + /** + * Apply the inverse of the linear transformation to the register set. + */ + private void InverseLT() + { + int x2 = RotateRight(X2, 22) ^ X3 ^ (X1 << 7); + int x0 = RotateRight(X0, 5) ^ X1 ^ X3; + int x3 = RotateRight(X3, 7); + int x1 = RotateRight(X1, 1); + X3 = x3 ^ x2 ^ x0 << 3; + X1 = x1 ^ x0 ^ x2; + X2 = RotateRight(x2, 3); + X0 = RotateRight(x0, 13); + } + } + +} diff --git a/src/core/srcbc/crypto/engines/SkipjackEngine.cs b/src/core/srcbc/crypto/engines/SkipjackEngine.cs new file mode 100644 index 0000000..12b848e --- /dev/null +++ b/src/core/srcbc/crypto/engines/SkipjackEngine.cs @@ -0,0 +1,255 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * a class that provides a basic SKIPJACK engine. + */ + public class SkipjackEngine + : IBlockCipher + { + const int BLOCK_SIZE = 8; + + static readonly short [] ftable = + { + 0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9, + 0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28, + 0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53, + 0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2, + 0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8, + 0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90, + 0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76, + 0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d, + 0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18, + 0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4, + 0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40, + 0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5, + 0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2, + 0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8, + 0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac, + 0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46 + }; + + private int[] key0, key1, key2, key3; + private bool encrypting; + + /** + * initialise a SKIPJACK cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to SKIPJACK init - " + parameters.GetType().ToString()); + + byte[] keyBytes = ((KeyParameter)parameters).GetKey(); + + this.encrypting = forEncryption; + this.key0 = new int[32]; + this.key1 = new int[32]; + this.key2 = new int[32]; + this.key3 = new int[32]; + + // + // expand the key to 128 bytes in 4 parts (saving us a modulo, multiply + // and an addition). + // + for (int i = 0; i < 32; i ++) + { + key0[i] = keyBytes[(i * 4) % 10] & 0xff; + key1[i] = keyBytes[(i * 4 + 1) % 10] & 0xff; + key2[i] = keyBytes[(i * 4 + 2) % 10] & 0xff; + key3[i] = keyBytes[(i * 4 + 3) % 10] & 0xff; + } + } + + public string AlgorithmName + { + get { return "SKIPJACK"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (key1 == null) + throw new InvalidOperationException("SKIPJACK engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, outOff); + } + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + /** + * The G permutation + */ + private int G( + int k, + int w) + { + int g1, g2, g3, g4, g5, g6; + + g1 = (w >> 8) & 0xff; + g2 = w & 0xff; + + g3 = ftable[g2 ^ key0[k]] ^ g1; + g4 = ftable[g3 ^ key1[k]] ^ g2; + g5 = ftable[g4 ^ key2[k]] ^ g3; + g6 = ftable[g5 ^ key3[k]] ^ g4; + + return ((g5 << 8) + g6); + } + + public int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int w1 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff); + int w2 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff); + int w3 = (input[inOff + 4] << 8) + (input[inOff + 5] & 0xff); + int w4 = (input[inOff + 6] << 8) + (input[inOff + 7] & 0xff); + + int k = 0; + + for (int t = 0; t < 2; t++) + { + for(int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w2; + w2 = G(k, w1); + w1 = w2 ^ tmp ^ (k + 1); + k++; + } + + for(int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w1 ^ w2 ^ (k + 1); + w2 = G(k, w1); + w1 = tmp; + k++; + } + } + + outBytes[outOff + 0] = (byte)((w1 >> 8)); + outBytes[outOff + 1] = (byte)(w1); + outBytes[outOff + 2] = (byte)((w2 >> 8)); + outBytes[outOff + 3] = (byte)(w2); + outBytes[outOff + 4] = (byte)((w3 >> 8)); + outBytes[outOff + 5] = (byte)(w3); + outBytes[outOff + 6] = (byte)((w4 >> 8)); + outBytes[outOff + 7] = (byte)(w4); + + return BLOCK_SIZE; + } + + /** + * the inverse of the G permutation. + */ + private int H( + int k, + int w) + { + int h1, h2, h3, h4, h5, h6; + + h1 = w & 0xff; + h2 = (w >> 8) & 0xff; + + h3 = ftable[h2 ^ key3[k]] ^ h1; + h4 = ftable[h3 ^ key2[k]] ^ h2; + h5 = ftable[h4 ^ key1[k]] ^ h3; + h6 = ftable[h5 ^ key0[k]] ^ h4; + + return ((h6 << 8) + h5); + } + + public int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int w2 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff); + int w1 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff); + int w4 = (input[inOff + 4] << 8) + (input[inOff + 5] & 0xff); + int w3 = (input[inOff + 6] << 8) + (input[inOff + 7] & 0xff); + + int k = 31; + + for (int t = 0; t < 2; t++) + { + for(int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w2; + w2 = H(k, w1); + w1 = w2 ^ tmp ^ (k + 1); + k--; + } + + for(int i = 0; i < 8; i++) + { + int tmp = w4; + w4 = w3; + w3 = w1 ^ w2 ^ (k + 1); + w2 = H(k, w1); + w1 = tmp; + k--; + } + } + + outBytes[outOff + 0] = (byte)((w2 >> 8)); + outBytes[outOff + 1] = (byte)(w2); + outBytes[outOff + 2] = (byte)((w1 >> 8)); + outBytes[outOff + 3] = (byte)(w1); + outBytes[outOff + 4] = (byte)((w4 >> 8)); + outBytes[outOff + 5] = (byte)(w4); + outBytes[outOff + 6] = (byte)((w3 >> 8)); + outBytes[outOff + 7] = (byte)(w3); + + return BLOCK_SIZE; + } + } + +} diff --git a/src/core/srcbc/crypto/engines/TEAEngine.cs b/src/core/srcbc/crypto/engines/TEAEngine.cs new file mode 100644 index 0000000..4733282 --- /dev/null +++ b/src/core/srcbc/crypto/engines/TEAEngine.cs @@ -0,0 +1,191 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * An TEA engine. + */ + public class TeaEngine + : IBlockCipher + { + private const int + rounds = 32, + block_size = 8, + key_size = 16, + delta = unchecked((int) 0x9E3779B9), + d_sum = unchecked((int) 0xC6EF3720); // sum on decrypt + + /* + * the expanded key array of 4 subkeys + */ + private int _a, _b, _c, _d; + private bool _initialised; + private bool _forEncryption; + + /** + * Create an instance of the TEA encryption algorithm + * and set some defaults + */ + public TeaEngine() + { + _initialised = false; + } + + public string AlgorithmName + { + get { return "TEA"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return block_size; + } + + /** + * initialise + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + { + throw new ArgumentException("invalid parameter passed to TEA init - " + + parameters.GetType().FullName); + } + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter) parameters; + + setKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + if ((inOff + block_size) > inBytes.Length) + throw new DataLengthException("input buffer too short"); + + if ((outOff + block_size) > outBytes.Length) + throw new DataLengthException("output buffer too short"); + + return _forEncryption + ? encryptBlock(inBytes, inOff, outBytes, outOff) + : decryptBlock(inBytes, inOff, outBytes, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @param key the key to be used + */ + private void setKey( + byte[] key) + { + _a = bytesToInt(key, 0); + _b = bytesToInt(key, 4); + _c = bytesToInt(key, 8); + _d = bytesToInt(key, 12); + } + + private int encryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(inBytes, inOff); + int v1 = bytesToInt(inBytes, inOff + 4); + + int sum = 0; + + for (int i = 0; i != rounds; i++) + { + sum += delta; +// v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b); + v0 += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((int)((uint)v1 >> 5) + _b); +// v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d); + v1 += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((int)((uint)v0 >> 5) + _d); + } + + unpackInt(v0, outBytes, outOff); + unpackInt(v1, outBytes, outOff + 4); + + return block_size; + } + + private int decryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(inBytes, inOff); + int v1 = bytesToInt(inBytes, inOff + 4); + + int sum = d_sum; + + for (int i = 0; i != rounds; i++) + { +// v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >>> 5) + _d); + v1 -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((int)((uint)v0 >> 5) + _d); +// v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >>> 5) + _b); + v0 -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((int)((uint)v1 >> 5) + _b); + sum -= delta; + } + + unpackInt(v0, outBytes, outOff); + unpackInt(v1, outBytes, outOff + 4); + + return block_size; + } + + private int bytesToInt( + byte[] b, + int inOff) + { + return ((b[inOff++]) << 24) + | ((b[inOff++] & 255) << 16) + | ((b[inOff++] & 255) << 8) + | ((b[inOff] & 255)); + } + + private void unpackInt( + int v, + byte[] b, + int outOff) + { + uint uv = (uint) v; + b[outOff++] = (byte)(uv >> 24); + b[outOff++] = (byte)(uv >> 16); + b[outOff++] = (byte)(uv >> 8); + b[outOff ] = (byte)uv; + } + } +} diff --git a/src/core/srcbc/crypto/engines/TwofishEngine.cs b/src/core/srcbc/crypto/engines/TwofishEngine.cs new file mode 100644 index 0000000..617b0a3 --- /dev/null +++ b/src/core/srcbc/crypto/engines/TwofishEngine.cs @@ -0,0 +1,673 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * A class that provides Twofish encryption operations. + * + * This Java implementation is based on the Java reference + * implementation provided by Bruce Schneier and developed + * by Raif S. Naffah. + */ + public sealed class TwofishEngine + : IBlockCipher + { + private static readonly byte[,] P = { + { // p0 + (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8, + (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76, + (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78, + (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38, + (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98, + (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C, + (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26, + (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48, + (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30, + (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23, + (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59, + (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82, + (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E, + (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C, + (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE, + (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61, + (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5, + (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B, + (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B, + (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1, + (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45, + (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66, + (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56, + (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7, + (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5, + (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA, + (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF, + (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71, + (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD, + (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8, + (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D, + (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7, + (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED, + (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2, + (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11, + (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90, + (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF, + (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB, + (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B, + (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF, + (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE, + (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B, + (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46, + (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64, + (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F, + (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A, + (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A, + (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A, + (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29, + (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02, + (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17, + (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D, + (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74, + (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72, + (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12, + (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34, + (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68, + (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8, + (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40, + (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4, + (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0, + (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00, + (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42, + (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0 }, + { // p1 + (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4, + (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8, + (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B, + (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B, + (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD, + (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1, + (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B, + (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F, + (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B, + (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D, + (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E, + (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5, + (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14, + (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3, + (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54, + (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51, + (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A, + (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96, + (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10, + (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C, + (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7, + (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70, + (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB, + (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8, + (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF, + (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC, + (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF, + (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2, + (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82, + (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9, + (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97, + (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17, + (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D, + (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3, + (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C, + (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E, + (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F, + (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49, + (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21, + (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9, + (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD, + (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01, + (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F, + (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48, + (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E, + (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19, + (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57, + (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64, + (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE, + (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5, + (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44, + (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69, + (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15, + (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E, + (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34, + (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC, + (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B, + (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB, + (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52, + (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9, + (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4, + (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2, + (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56, + (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 } + }; + + /** + * Define the fixed p0/p1 permutations used in keyed S-box lookup. + * By changing the following constant definitions, the S-boxes will + * automatically Get changed in the Twofish engine. + */ + private const int P_00 = 1; + private const int P_01 = 0; + private const int P_02 = 0; + private const int P_03 = P_01 ^ 1; + private const int P_04 = 1; + + private const int P_10 = 0; + private const int P_11 = 0; + private const int P_12 = 1; + private const int P_13 = P_11 ^ 1; + private const int P_14 = 0; + + private const int P_20 = 1; + private const int P_21 = 1; + private const int P_22 = 0; + private const int P_23 = P_21 ^ 1; + private const int P_24 = 0; + + private const int P_30 = 0; + private const int P_31 = 1; + private const int P_32 = 1; + private const int P_33 = P_31 ^ 1; + private const int P_34 = 1; + + /* Primitive polynomial for GF(256) */ + private const int GF256_FDBK = 0x169; + private const int GF256_FDBK_2 = GF256_FDBK / 2; + private const int GF256_FDBK_4 = GF256_FDBK / 4; + + private const int RS_GF_FDBK = 0x14D; // field generator + + //==================================== + // Useful constants + //==================================== + + private const int ROUNDS = 16; + private const int MAX_ROUNDS = 16; // bytes = 128 bits + private const int BLOCK_SIZE = 16; // bytes = 128 bits + private const int MAX_KEY_BITS = 256; + + private const int INPUT_WHITEN=0; + private const int OUTPUT_WHITEN=INPUT_WHITEN+BLOCK_SIZE/4; // 4 + private const int ROUND_SUBKEYS=OUTPUT_WHITEN+BLOCK_SIZE/4;// 8 + + private const int TOTAL_SUBKEYS=ROUND_SUBKEYS+2*MAX_ROUNDS;// 40 + + private const int SK_STEP = 0x02020202; + private const int SK_BUMP = 0x01010101; + private const int SK_ROTL = 9; + + private bool encrypting; + + private int[] gMDS0 = new int[MAX_KEY_BITS]; + private int[] gMDS1 = new int[MAX_KEY_BITS]; + private int[] gMDS2 = new int[MAX_KEY_BITS]; + private int[] gMDS3 = new int[MAX_KEY_BITS]; + + /** + * gSubKeys[] and gSBox[] are eventually used in the + * encryption and decryption methods. + */ + private int[] gSubKeys; + private int[] gSBox; + + private int k64Cnt; + + private byte[] workingKey; + + public TwofishEngine() + { + // calculate the MDS matrix + int[] m1 = new int[2]; + int[] mX = new int[2]; + int[] mY = new int[2]; + int j; + + for (int i=0; i< MAX_KEY_BITS ; i++) + { + j = P[0,i] & 0xff; + m1[0] = j; + mX[0] = Mx_X(j) & 0xff; + mY[0] = Mx_Y(j) & 0xff; + + j = P[1,i] & 0xff; + m1[1] = j; + mX[1] = Mx_X(j) & 0xff; + mY[1] = Mx_Y(j) & 0xff; + + gMDS0[i] = m1[P_00] | mX[P_00] << 8 | + mY[P_00] << 16 | mY[P_00] << 24; + + gMDS1[i] = mY[P_10] | mY[P_10] << 8 | + mX[P_10] << 16 | m1[P_10] << 24; + + gMDS2[i] = mX[P_20] | mY[P_20] << 8 | + m1[P_20] << 16 | mY[P_20] << 24; + + gMDS3[i] = mX[P_30] | m1[P_30] << 8 | + mY[P_30] << 16 | mX[P_30] << 24; + } + } + + /** + * initialise a Twofish cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to Twofish init - " + parameters.GetType().ToString()); + + this.encrypting = forEncryption; + this.workingKey = ((KeyParameter)parameters).GetKey(); + this.k64Cnt = (this.workingKey.Length / 8); // pre-padded ? + SetKey(this.workingKey); + } + + public string AlgorithmName + { + get { return "Twofish"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + throw new InvalidOperationException("Twofish not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, outOff); + } + + return BLOCK_SIZE; + } + + public void Reset() + { + if (this.workingKey != null) + { + SetKey(this.workingKey); + } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + private void SetKey(byte[] key) + { + int[] k32e = new int[MAX_KEY_BITS/64]; // 4 + int[] k32o = new int[MAX_KEY_BITS/64]; // 4 + + int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4 + gSubKeys = new int[TOTAL_SUBKEYS]; + + if (k64Cnt < 1) + { + throw new ArgumentException("Key size less than 64 bits"); + } + + if (k64Cnt > 4) + { + throw new ArgumentException("Key size larger than 256 bits"); + } + + /* + * k64Cnt is the number of 8 byte blocks (64 chunks) + * that are in the input key. The input key is a + * maximum of 32 bytes ( 256 bits ), so the range + * for k64Cnt is 1..4 + */ + for (int i=0,p=0; i> 24); + A += B; + gSubKeys[i*2] = A; + A += B; + gSubKeys[i*2 + 1] = A << SK_ROTL | (int)((uint)A >> (32-SK_ROTL)); + } + + /* + * fully expand the table for speed + */ + int k0 = sBoxKeys[0]; + int k1 = sBoxKeys[1]; + int k2 = sBoxKeys[2]; + int k3 = sBoxKeys[3]; + int b0, b1, b2, b3; + gSBox = new int[4*MAX_KEY_BITS]; + for (int i=0; i>1) | x2 << 31; + x3 = (x3 << 1 | (int) ((uint)x3 >> 31)) ^ (t0 + 2*t1 + gSubKeys[k++]); + + t0 = Fe32_0(x2); + t1 = Fe32_3(x3); + x0 ^= t0 + t1 + gSubKeys[k++]; + x0 = (int) ((uint)x0 >>1) | x0 << 31; + x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + 2*t1 + gSubKeys[k++]); + } + + Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex); + Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4); + Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8); + Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12); + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * The input will be an exact multiple of our blocksize. + */ + private void DecryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int x2 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN]; + int x3 = BytesTo32Bits(src, srcIndex+4) ^ gSubKeys[OUTPUT_WHITEN + 1]; + int x0 = BytesTo32Bits(src, srcIndex+8) ^ gSubKeys[OUTPUT_WHITEN + 2]; + int x1 = BytesTo32Bits(src, srcIndex+12) ^ gSubKeys[OUTPUT_WHITEN + 3]; + + int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ; + int t0, t1; + for (int r = 0; r< ROUNDS ; r +=2) + { + t0 = Fe32_0(x2); + t1 = Fe32_3(x3); + x1 ^= t0 + 2*t1 + gSubKeys[k--]; + x0 = (x0 << 1 | (int)((uint) x0 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); + x1 = (int) ((uint)x1 >>1) | x1 << 31; + + t0 = Fe32_0(x0); + t1 = Fe32_3(x1); + x3 ^= t0 + 2*t1 + gSubKeys[k--]; + x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); + x3 = (int)((uint)x3 >>1) | x3 << 31; + } + + Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex); + Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4); + Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8); + Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12); + } + + /* + * TODO: This can be optimised and made cleaner by combining + * the functionality in this function and applying it appropriately + * to the creation of the subkeys during key setup. + */ + private int F32(int x, int[] k32) + { + int b0 = M_b0(x); + int b1 = M_b1(x); + int b2 = M_b2(x); + int b3 = M_b3(x); + int k0 = k32[0]; + int k1 = k32[1]; + int k2 = k32[2]; + int k3 = k32[3]; + + int result = 0; + switch (k64Cnt & 3) + { + case 1: + result = gMDS0[(P[P_01,b0] & 0xff) ^ M_b0(k0)] ^ + gMDS1[(P[P_11,b1] & 0xff) ^ M_b1(k0)] ^ + gMDS2[(P[P_21,b2] & 0xff) ^ M_b2(k0)] ^ + gMDS3[(P[P_31,b3] & 0xff) ^ M_b3(k0)]; + break; + case 0: /* 256 bits of key */ + b0 = (P[P_04,b0] & 0xff) ^ M_b0(k3); + b1 = (P[P_14,b1] & 0xff) ^ M_b1(k3); + b2 = (P[P_24,b2] & 0xff) ^ M_b2(k3); + b3 = (P[P_34,b3] & 0xff) ^ M_b3(k3); + goto case 3; + case 3: + b0 = (P[P_03,b0] & 0xff) ^ M_b0(k2); + b1 = (P[P_13,b1] & 0xff) ^ M_b1(k2); + b2 = (P[P_23,b2] & 0xff) ^ M_b2(k2); + b3 = (P[P_33,b3] & 0xff) ^ M_b3(k2); + goto case 2; + case 2: + result = + gMDS0[(P[P_01,(P[P_02,b0]&0xff)^M_b0(k1)]&0xff)^M_b0(k0)] ^ + gMDS1[(P[P_11,(P[P_12,b1]&0xff)^M_b1(k1)]&0xff)^M_b1(k0)] ^ + gMDS2[(P[P_21,(P[P_22,b2]&0xff)^M_b2(k1)]&0xff)^M_b2(k0)] ^ + gMDS3[(P[P_31,(P[P_32,b3]&0xff)^M_b3(k1)]&0xff)^M_b3(k0)]; + break; + } + return result; + } + + /** + * Use (12, 8) Reed-Solomon code over GF(256) to produce + * a key S-box 32-bit entity from 2 key material 32-bit + * entities. + * + * @param k0 first 32-bit entity + * @param k1 second 32-bit entity + * @return Remainder polynomial Generated using RS code + */ + private int RS_MDS_Encode(int k0, int k1) + { + int r = k1; + for (int i = 0 ; i < 4 ; i++) // shift 1 byte at a time + { + r = RS_rem(r); + } + r ^= k0; + for (int i=0 ; i < 4 ; i++) + { + r = RS_rem(r); + } + + return r; + } + + /** + * Reed-Solomon code parameters: (12,8) reversible code: + *

+ *

+        * G(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
+        * 
+ * where a = primitive root of field generator 0x14D + *

+ */ + private int RS_rem(int x) + { + int b = (int) (((uint)x >> 24) & 0xff); + int g2 = ((b << 1) ^ + ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; + int g3 = ( (int)((uint)b >> 1) ^ + ((b & 0x01) != 0 ? (int)((uint)RS_GF_FDBK >> 1) : 0)) ^ g2 ; + return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b); + } + + private int LFSR1(int x) + { + return (x >> 1) ^ + (((x & 0x01) != 0) ? GF256_FDBK_2 : 0); + } + + private int LFSR2(int x) + { + return (x >> 2) ^ + (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^ + (((x & 0x01) != 0) ? GF256_FDBK_4 : 0); + } + + private int Mx_X(int x) + { + return x ^ LFSR2(x); + } // 5B + + private int Mx_Y(int x) + { + return x ^ LFSR1(x) ^ LFSR2(x); + } // EF + + private int M_b0(int x) + { + return x & 0xff; + } + + private int M_b1(int x) + { + return (int)((uint)x >> 8) & 0xff; + } + + private int M_b2(int x) + { + return (int)((uint)x >> 16) & 0xff; + } + + private int M_b3(int x) + { + return (int)((uint)x >> 24) & 0xff; + } + + private int Fe32_0(int x) + { + return gSBox[ 0x000 + 2*(x & 0xff) ] ^ + gSBox[ 0x001 + 2*((int)((uint)x >> 8) & 0xff) ] ^ + gSBox[ 0x200 + 2*((int)((uint)x >> 16) & 0xff) ] ^ + gSBox[ 0x201 + 2*((int)((uint)x >> 24) & 0xff) ]; + } + + private int Fe32_3(int x) + { + return gSBox[ 0x000 + 2*((int)((uint)x >> 24) & 0xff) ] ^ + gSBox[ 0x001 + 2*(x & 0xff) ] ^ + gSBox[ 0x200 + 2*((int)((uint)x >> 8) & 0xff) ] ^ + gSBox[ 0x201 + 2*((int)((uint)x >> 16) & 0xff) ]; + } + + private int BytesTo32Bits(byte[] b, int p) + { + return ((b[p] & 0xff) ) | + ((b[p+1] & 0xff) << 8) | + ((b[p+2] & 0xff) << 16) | + ((b[p+3] & 0xff) << 24); + } + + private void Bits32ToBytes(int inData, byte[] b, int offset) + { + b[offset] = (byte)inData; + b[offset + 1] = (byte)(inData >> 8); + b[offset + 2] = (byte)(inData >> 16); + b[offset + 3] = (byte)(inData >> 24); + } + } + +} diff --git a/src/core/srcbc/crypto/engines/VMPCEngine.cs b/src/core/srcbc/crypto/engines/VMPCEngine.cs new file mode 100644 index 0000000..df79593 --- /dev/null +++ b/src/core/srcbc/crypto/engines/VMPCEngine.cs @@ -0,0 +1,139 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + public class VmpcEngine + : IStreamCipher + { + /* + * variables to hold the state of the VMPC engine during encryption and + * decryption + */ + protected byte n = 0; + protected byte[] P = null; + protected byte s = 0; + + protected byte[] workingIV; + protected byte[] workingKey; + + public virtual string AlgorithmName + { + get { return "VMPC"; } + } + + /** + * initialise a VMPC cipher. + * + * @param forEncryption + * whether or not we are for encryption. + * @param params + * the parameters required to set up the cipher. + * @exception ArgumentException + * if the params argument is inappropriate. + */ + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is ParametersWithIV)) + throw new ArgumentException("VMPC Init parameters must include an IV"); + + ParametersWithIV ivParams = (ParametersWithIV) parameters; + KeyParameter key = (KeyParameter) ivParams.Parameters; + + if (!(ivParams.Parameters is KeyParameter)) + throw new ArgumentException("VMPC Init parameters must include a key"); + + this.workingIV = ivParams.GetIV(); + + if (workingIV == null || workingIV.Length < 1 || workingIV.Length > 768) + throw new ArgumentException("VMPC requires 1 to 768 bytes of IV"); + + this.workingKey = key.GetKey(); + + InitKey(this.workingKey, this.workingIV); + } + + protected virtual void InitKey( + byte[] keyBytes, + byte[] ivBytes) + { + s = 0; + P = new byte[256]; + for (int i = 0; i < 256; i++) + { + P[i] = (byte) i; + } + + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + n = 0; + } + + public virtual void ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if ((inOff + len) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + for (int i = 0; i < len; i++) + { + s = P[(s + P[n & 0xff]) & 0xff]; + byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + // encryption + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte) ((n + 1) & 0xff); + + // xor + output[i + outOff] = (byte) (input[i + inOff] ^ z); + } + } + + public virtual void Reset() + { + InitKey(this.workingKey, this.workingIV); + } + + public virtual byte ReturnByte( + byte input) + { + s = P[(s + P[n & 0xff]) & 0xff]; + byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + // encryption + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte) ((n + 1) & 0xff); + + // xor + return (byte) (input ^ z); + } + } +} diff --git a/src/core/srcbc/crypto/engines/VMPCKSA3Engine.cs b/src/core/srcbc/crypto/engines/VMPCKSA3Engine.cs new file mode 100644 index 0000000..7e0c64b --- /dev/null +++ b/src/core/srcbc/crypto/engines/VMPCKSA3Engine.cs @@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Engines +{ + public class VmpcKsa3Engine + : VmpcEngine + { + public override string AlgorithmName + { + get { return "VMPC-KSA3"; } + } + + protected override void InitKey( + byte[] keyBytes, + byte[] ivBytes) + { + s = 0; + P = new byte[256]; + for (int i = 0; i < 256; i++) + { + P[i] = (byte) i; + } + + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + n = 0; + } + } +} diff --git a/src/core/srcbc/crypto/engines/XTEAEngine.cs b/src/core/srcbc/crypto/engines/XTEAEngine.cs new file mode 100644 index 0000000..0d4036f --- /dev/null +++ b/src/core/srcbc/crypto/engines/XTEAEngine.cs @@ -0,0 +1,185 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * An XTEA engine. + */ + public class XteaEngine + : IBlockCipher + { + private const int + rounds = 32, + block_size = 8, + key_size = 16, + delta = unchecked((int) 0x9E3779B9), + d_sum = unchecked((int) 0xC6EF3720); // sum on decrypt + + /* + * the expanded key array of 4 subkeys + */ + private int[] _S = new int[4]; + private bool _initialised; + private bool _forEncryption; + + /** + * Create an instance of the TEA encryption algorithm + * and set some defaults + */ + public XteaEngine() + { + _initialised = false; + } + + public string AlgorithmName + { + get { return "XTEA"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return block_size; + } + + /** + * initialise + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + { + throw new ArgumentException("invalid parameter passed to TEA init - " + + parameters.GetType().FullName); + } + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter) parameters; + + setKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + if ((inOff + block_size) > inBytes.Length) + throw new DataLengthException("input buffer too short"); + + if ((outOff + block_size) > outBytes.Length) + throw new DataLengthException("output buffer too short"); + + return _forEncryption + ? encryptBlock(inBytes, inOff, outBytes, outOff) + : decryptBlock(inBytes, inOff, outBytes, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @param key the key to be used + */ + private void setKey( + byte[] key) + { + _S[0] = bytesToInt(key, 0); + _S[1] = bytesToInt(key, 4); + _S[2] = bytesToInt(key, 8); + _S[3] = bytesToInt(key, 12); + } + + private int encryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(inBytes, inOff); + int v1 = bytesToInt(inBytes, inOff + 4); + + int sum = 0; + + for (int i = 0; i != rounds; i++) + { + v0 += ((v1 << 4 ^ (int)((uint)v1 >> 5)) + v1) ^ (sum + _S[sum & 3]); + sum += delta; + v1 += ((v0 << 4 ^ (int)((uint)v0 >> 5)) + v0) ^ (sum + _S[(int)((uint)sum >> 11) & 3]); + } + + unpackInt(v0, outBytes, outOff); + unpackInt(v1, outBytes, outOff + 4); + + return block_size; + } + + private int decryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + int v0 = bytesToInt(inBytes, inOff); + int v1 = bytesToInt(inBytes, inOff + 4); + + int sum = d_sum; + + for (int i = 0; i != rounds; i++) + { + v1 -= ((v0 << 4 ^ (int)((uint)v0 >> 5)) + v0) ^ (sum + _S[(int)((uint)sum >> 11) & 3]); + sum -= delta; + v0 -= ((v1 << 4 ^ (int)((uint)v1 >> 5)) + v1) ^ (sum + _S[sum & 3]); + } + + unpackInt(v0, outBytes, outOff); + unpackInt(v1, outBytes, outOff + 4); + + return block_size; + } + + private int bytesToInt(byte[] b, int inOff) + { + return ((b[inOff++]) << 24) | + ((b[inOff++] & 255) << 16) | + ((b[inOff++] & 255) << 8) | + ((b[inOff] & 255)); + } + + private void unpackInt( + int v, + byte[] b, + int outOff) + { + uint uv = (uint) v; + b[outOff++] = (byte)(uv >> 24); + b[outOff++] = (byte)(uv >> 16); + b[outOff++] = (byte)(uv >> 8); + b[outOff ] = (byte)uv; + } + } +} diff --git a/src/core/srcbc/crypto/generators/BaseKdfBytesGenerator.cs b/src/core/srcbc/crypto/generators/BaseKdfBytesGenerator.cs new file mode 100644 index 0000000..56e23d4 --- /dev/null +++ b/src/core/srcbc/crypto/generators/BaseKdfBytesGenerator.cs @@ -0,0 +1,141 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Basic KDF generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033 + *
+ * This implementation is based on ISO 18033/P1363a. + */ + public class BaseKdfBytesGenerator + : IDerivationFunction + { + private int counterStart; + private IDigest digest; + private byte[] shared; + private byte[] iv; + + /** + * Construct a KDF Parameters generator. + * + * @param counterStart value of counter. + * @param digest the digest to be used as the source of derived keys. + */ + protected BaseKdfBytesGenerator( + int counterStart, + IDigest digest) + { + this.counterStart = counterStart; + this.digest = digest; + } + + public void Init( + IDerivationParameters parameters) + { + if (parameters is KdfParameters) + { + KdfParameters p = (KdfParameters)parameters; + + shared = p.GetSharedSecret(); + iv = p.GetIV(); + } + else if (parameters is Iso18033KdfParameters) + { + Iso18033KdfParameters p = (Iso18033KdfParameters)parameters; + + shared = p.GetSeed(); + iv = null; + } + else + { + throw new ArgumentException("KDF parameters required for KDF Generator"); + } + } + + /** + * return the underlying digest. + */ + public IDigest Digest + { + get + { + return digest; + } + } + + /** + * fill len bytes of the output buffer with bytes generated from + * the derivation function. + * + * @throws ArgumentException if the size of the request will cause an overflow. + * @throws DataLengthException if the out buffer is too small. + */ + public int GenerateBytes( + byte[] output, + int outOff, + int length) + { + if ((output.Length - length) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + long oBytes = length; + int outLen = digest.GetDigestSize(); + + // + // this is at odds with the standard implementation, the + // maximum value should be hBits * (2^32 - 1) where hBits + // is the digest output size in bits. We can't have an + // array with a long index at the moment... + // + if (oBytes > ((2L << 32) - 1)) + { + throw new ArgumentException("Output length too large"); + } + + int cThreshold = (int)((oBytes + outLen - 1) / outLen); + + byte[] dig = new byte[digest.GetDigestSize()]; + + int counter = counterStart; + + for (int i = 0; i < cThreshold; i++) + { + digest.BlockUpdate(shared, 0, shared.Length); + + digest.Update((byte)(counter >> 24)); + digest.Update((byte)(counter >> 16)); + digest.Update((byte)(counter >> 8)); + digest.Update((byte)counter); + + if (iv != null) + { + digest.BlockUpdate(iv, 0, iv.Length); + } + + digest.DoFinal(dig, 0); + + if (length > outLen) + { + Array.Copy(dig, 0, output, outOff, outLen); + outOff += outLen; + length -= outLen; + } + else + { + Array.Copy(dig, 0, output, outOff, length); + } + + counter++; + } + + digest.Reset(); + + return (int)oBytes; + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/crypto/generators/DHBasicKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/DHBasicKeyPairGenerator.cs new file mode 100644 index 0000000..843587e --- /dev/null +++ b/src/core/srcbc/crypto/generators/DHBasicKeyPairGenerator.cs @@ -0,0 +1,40 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a basic Diffie-Helman key pair generator. + * + * This Generates keys consistent for use with the basic algorithm for + * Diffie-Helman. + */ + public class DHBasicKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private DHKeyGenerationParameters param; + + public virtual void Init( + KeyGenerationParameters parameters) + { + this.param = (DHKeyGenerationParameters) parameters; + } + + public virtual AsymmetricCipherKeyPair GenerateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance; + DHParameters dhParams = param.Parameters; + + BigInteger p = dhParams.P; + BigInteger x = helper.CalculatePrivate(p, param.Random, dhParams.L); + BigInteger y = helper.CalculatePublic(p, dhParams.G, x); + + return new AsymmetricCipherKeyPair( + new DHPublicKeyParameters(y, dhParams), + new DHPrivateKeyParameters(x, dhParams)); + } + } + +} diff --git a/src/core/srcbc/crypto/generators/DHKeyGeneratorHelper.cs b/src/core/srcbc/crypto/generators/DHKeyGeneratorHelper.cs new file mode 100644 index 0000000..4da5985 --- /dev/null +++ b/src/core/srcbc/crypto/generators/DHKeyGeneratorHelper.cs @@ -0,0 +1,78 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + class DHKeyGeneratorHelper + { + private const int MAX_ITERATIONS = 1000; + + internal static readonly DHKeyGeneratorHelper Instance = new DHKeyGeneratorHelper(); + + private DHKeyGeneratorHelper() + { + } + + internal BigInteger CalculatePrivate( + BigInteger p, + SecureRandom random, + int limit) + { + // + // calculate the private key + // + BigInteger pSub2 = p.Subtract(BigInteger.Two); + BigInteger x; + + if (limit == 0) + { + x = createInRange(pSub2, random); + } + else + { + do + { + // TODO Check this (should the generated numbers always be odd, + // and length 'limit'?) + x = new BigInteger(limit, 0, random); + } + while (x.SignValue == 0); + } + + return x; + } + + private BigInteger createInRange( + BigInteger max, + SecureRandom random) + { + BigInteger x; + int maxLength = max.BitLength; + int count = 0; + + do + { + x = new BigInteger(maxLength, random); + count++; + } + while ((x.SignValue == 0 || x.CompareTo(max) > 0) && count != MAX_ITERATIONS); + + if (count == MAX_ITERATIONS) // fall back to a faster (restricted) method + { + return new BigInteger(maxLength - 1, random).SetBit(0); + } + + return x; + } + + internal BigInteger CalculatePublic( + BigInteger p, + BigInteger g, + BigInteger x) + { + return g.ModPow(x, p); + } + } +} diff --git a/src/core/srcbc/crypto/generators/DHKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/DHKeyPairGenerator.cs new file mode 100644 index 0000000..d22ffe7 --- /dev/null +++ b/src/core/srcbc/crypto/generators/DHKeyPairGenerator.cs @@ -0,0 +1,40 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a Diffie-Helman key pair generator. + * + * This Generates keys consistent for use in the MTI/A0 key agreement protocol + * as described in "Handbook of Applied Cryptography", Pages 516-519. + */ + public class DHKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private DHKeyGenerationParameters param; + + public virtual void Init( + KeyGenerationParameters parameters) + { + this.param = (DHKeyGenerationParameters) parameters; + } + + public virtual AsymmetricCipherKeyPair GenerateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance; + DHParameters dhParams = param.Parameters; + + BigInteger p = dhParams.P; + BigInteger x = helper.CalculatePrivate(p, param.Random, dhParams.L); + BigInteger y = helper.CalculatePublic(p, dhParams.G, x); + + return new AsymmetricCipherKeyPair( + new DHPublicKeyParameters(y, dhParams), + new DHPrivateKeyParameters(x, dhParams)); + } + } + +} diff --git a/src/core/srcbc/crypto/generators/DHParametersGenerator.cs b/src/core/srcbc/crypto/generators/DHParametersGenerator.cs new file mode 100644 index 0000000..c4d39bd --- /dev/null +++ b/src/core/srcbc/crypto/generators/DHParametersGenerator.cs @@ -0,0 +1,45 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class DHParametersGenerator + { + private int size; + private int certainty; + private SecureRandom random; + + public virtual 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 DHParameters object. + *

+ * Note: can take a while...

+ */ + public virtual DHParameters GenerateParameters() + { + // + // find a safe prime p where p = 2*q + 1, where p and q are prime. + // + BigInteger[] safePrimes = DHParametersHelper.GenerateSafePrimes(size, certainty, random); + + BigInteger p = safePrimes[0]; + BigInteger q = safePrimes[1]; + BigInteger g = DHParametersHelper.SelectGenerator(p, q, random); + + return new DHParameters(p, g, q, BigInteger.Two, null); + } + } +} diff --git a/src/core/srcbc/crypto/generators/DHParametersHelper.cs b/src/core/srcbc/crypto/generators/DHParametersHelper.cs new file mode 100644 index 0000000..25076c1 --- /dev/null +++ b/src/core/srcbc/crypto/generators/DHParametersHelper.cs @@ -0,0 +1,244 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + internal class DHParametersHelper + { + // The primes b/w 2 and ~2^10 + /* + 3 5 7 11 13 17 19 23 29 + 31 37 41 43 47 53 59 61 67 71 + 73 79 83 89 97 101 103 107 109 113 + 127 131 137 139 149 151 157 163 167 173 + 179 181 191 193 197 199 211 223 227 229 + 233 239 241 251 257 263 269 271 277 281 + 283 293 307 311 313 317 331 337 347 349 + 353 359 367 373 379 383 389 397 401 409 + 419 421 431 433 439 443 449 457 461 463 + 467 479 487 491 499 503 509 521 523 541 + 547 557 563 569 571 577 587 593 599 601 + 607 613 617 619 631 641 643 647 653 659 + 661 673 677 683 691 701 709 719 727 733 + 739 743 751 757 761 769 773 787 797 809 + 811 821 823 827 829 839 853 857 859 863 + 877 881 883 887 907 911 919 929 937 941 + 947 953 967 971 977 983 991 997 + 1009 1013 1019 1021 1031 + */ + + // Each list has a product < 2^31 + private static readonly int[][] primeLists = new int[][] + { + new int[]{ 3, 5, 7, 11, 13, 17, 19, 23 }, + new int[]{ 29, 31, 37, 41, 43 }, + new int[]{ 47, 53, 59, 61, 67 }, + new int[]{ 71, 73, 79, 83 }, + new int[]{ 89, 97, 101, 103 }, + + new int[]{ 107, 109, 113, 127 }, + new int[]{ 131, 137, 139, 149 }, + new int[]{ 151, 157, 163, 167 }, + new int[]{ 173, 179, 181, 191 }, + new int[]{ 193, 197, 199, 211 }, + + new int[]{ 223, 227, 229 }, + new int[]{ 233, 239, 241 }, + new int[]{ 251, 257, 263 }, + new int[]{ 269, 271, 277 }, + new int[]{ 281, 283, 293 }, + + new int[]{ 307, 311, 313 }, + new int[]{ 317, 331, 337 }, + new int[]{ 347, 349, 353 }, + new int[]{ 359, 367, 373 }, + new int[]{ 379, 383, 389 }, + + new int[]{ 397, 401, 409 }, + new int[]{ 419, 421, 431 }, + new int[]{ 433, 439, 443 }, + new int[]{ 449, 457, 461 }, + new int[]{ 463, 467, 479 }, + + new int[]{ 487, 491, 499 }, + new int[]{ 503, 509, 521 }, + new int[]{ 523, 541, 547 }, + new int[]{ 557, 563, 569 }, + new int[]{ 571, 577, 587 }, + + new int[]{ 593, 599, 601 }, + new int[]{ 607, 613, 617 }, + new int[]{ 619, 631, 641 }, + new int[]{ 643, 647, 653 }, + new int[]{ 659, 661, 673 }, + + new int[]{ 677, 683, 691 }, + new int[]{ 701, 709, 719 }, + new int[]{ 727, 733, 739 }, + new int[]{ 743, 751, 757 }, + new int[]{ 761, 769, 773 }, + + new int[]{ 787, 797, 809 }, + new int[]{ 811, 821, 823 }, + new int[]{ 827, 829, 839 }, + new int[]{ 853, 857, 859 }, + new int[]{ 863, 877, 881 }, + + new int[]{ 883, 887, 907 }, + new int[]{ 911, 919, 929 }, + new int[]{ 937, 941, 947 }, + new int[]{ 953, 967, 971 }, + new int[]{ 977, 983, 991 }, + + new int[]{ 997, 1009, 1013 }, + new int[]{ 1019, 1021, 1031 }, + }; + + private static readonly BigInteger Six = BigInteger.ValueOf(6); + + private static readonly int[] primeProducts; + private static readonly BigInteger[] PrimeProducts; + + static DHParametersHelper() + { + primeProducts = new int[primeLists.Length]; + PrimeProducts = new BigInteger[primeLists.Length]; + + for (int i = 0; i < primeLists.Length; ++i) + { + int[] primeList = primeLists[i]; + int product = 1; + for (int j = 0; j < primeList.Length; ++j) + { + product *= primeList[j]; + } + primeProducts[i] = product; + PrimeProducts[i] = BigInteger.ValueOf(product); + } + } + + // Finds a pair of prime BigInteger's {p, q: p = 2q + 1} + internal static BigInteger[] GenerateSafePrimes( + int size, + int certainty, + SecureRandom random) + { + BigInteger p, q; + int qLength = size - 1; + + if (size <= 32) + { + for (;;) + { + q = new BigInteger(qLength, 2, random); + + p = q.ShiftLeft(1).Add(BigInteger.One); + + if (p.IsProbablePrime(certainty) + && (certainty <= 2 || q.IsProbablePrime(certainty))) + break; + } + } + else + { + // Note: Modified from Java version for speed + for (;;) + { + q = new BigInteger(qLength, 0, random); + + retry: + for (int i = 0; i < primeLists.Length; ++i) + { + int test = q.Remainder(PrimeProducts[i]).IntValue; + + if (i == 0) + { + int rem3 = test % 3; + if (rem3 != 2) + { + int diff = 2 * rem3 + 2; + q = q.Add(BigInteger.ValueOf(diff)); + test = (test + diff) % primeProducts[i]; + } + } + + int[] primeList = primeLists[i]; + for (int j = 0; j < primeList.Length; ++j) + { + int prime = primeList[j]; + int qRem = test % prime; + if (qRem == 0 || qRem == (prime >> 1)) + { + q = q.Add(Six); + goto retry; + } + } + } + + + if (q.BitLength != qLength) + continue; + + if (!q.RabinMillerTest(2, random)) + continue; + + p = q.ShiftLeft(1).Add(BigInteger.One); + + if (p.RabinMillerTest(certainty, random) + && (certainty <= 2 || q.RabinMillerTest(certainty - 2, random))) + break; + } + } + + return new BigInteger[] { p, q }; + } + + // Select a high order element of the multiplicative group Zp* + // p and q must be s.t. p = 2*q + 1, where p and q are prime + internal static BigInteger SelectGenerator( + BigInteger p, + BigInteger q, + SecureRandom random) + { + BigInteger pMinusTwo = p.Subtract(BigInteger.Two); + BigInteger g; + + // Handbook of Applied Cryptography 4.86 + do + { + g = CreateInRange(BigInteger.Two, pMinusTwo, random); + } + while (g.ModPow(BigInteger.Two, p).Equals(BigInteger.One) + || g.ModPow(q, p).Equals(BigInteger.One)); + +/* + // RFC 2631 2.1.1 (and see Handbook of Applied Cryptography 4.81) + do + { + BigInteger h = CreateInRange(BigInteger.Two, pMinusTwo, random); + + g = h.ModPow(BigInteger.Two, p); + } + while (g.Equals(BigInteger.One)); +*/ + + return g; + } + + private static BigInteger CreateInRange( + BigInteger min, + BigInteger max, + SecureRandom random) + { + BigInteger x; + do + { + x = new BigInteger(max.BitLength, random); + } + while (x.CompareTo(min) < 0 || x.CompareTo(max) > 0); + return x; + } + } +} diff --git a/src/core/srcbc/crypto/generators/DesEdeKeyGenerator.cs b/src/core/srcbc/crypto/generators/DesEdeKeyGenerator.cs new file mode 100644 index 0000000..10ef4e7 --- /dev/null +++ b/src/core/srcbc/crypto/generators/DesEdeKeyGenerator.cs @@ -0,0 +1,66 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class DesEdeKeyGenerator + : DesKeyGenerator + { + public DesEdeKeyGenerator() + { + } + + internal DesEdeKeyGenerator( + int defaultStrength) + : base(defaultStrength) + { + } + + /** + * initialise the key generator - if strength is set to zero + * the key Generated will be 192 bits in size, otherwise + * strength can be 128 or 192 (or 112 or 168 if you don't count + * parity bits), depending on whether you wish to do 2-key or 3-key + * triple DES. + * + * @param param the parameters to be used for key generation + */ + protected override void engineInit( + KeyGenerationParameters parameters) + { + base.engineInit(parameters); + + if (strength == 0 || strength == (168 / 8)) + { + strength = DesEdeParameters.DesEdeKeyLength; + } + else if (strength == (112 / 8)) + { + strength = 2 * DesEdeParameters.DesKeyLength; + } + else if (strength != DesEdeParameters.DesEdeKeyLength + && strength != (2 * DesEdeParameters.DesKeyLength)) + { + throw new ArgumentException("DESede key must be " + + (DesEdeParameters.DesEdeKeyLength * 8) + " or " + + (2 * 8 * DesEdeParameters.DesKeyLength) + + " bits long."); + } + } + + protected override byte[] engineGenerateKey() + { + byte[] newKey; + + do + { + newKey = random.GenerateSeed(strength); + DesEdeParameters.SetOddParity(newKey); + } + while (DesEdeParameters.IsWeakKey(newKey, 0, newKey.Length)); + + return newKey; + } + } +} diff --git a/src/core/srcbc/crypto/generators/DesKeyGenerator.cs b/src/core/srcbc/crypto/generators/DesKeyGenerator.cs new file mode 100644 index 0000000..12dcc9d --- /dev/null +++ b/src/core/srcbc/crypto/generators/DesKeyGenerator.cs @@ -0,0 +1,34 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class DesKeyGenerator + : CipherKeyGenerator + { + public DesKeyGenerator() + { + } + + internal DesKeyGenerator( + int defaultStrength) + : base(defaultStrength) + { + } + + protected override byte[] engineGenerateKey() + { + byte[] newKey; + + do + { + newKey = random.GenerateSeed(DesParameters.DesKeyLength); + DesParameters.SetOddParity(newKey); + } + while (DesParameters.IsWeakKey(newKey, 0)); + + return newKey; + } + } +} diff --git a/src/core/srcbc/crypto/generators/DsaKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/DsaKeyPairGenerator.cs new file mode 100644 index 0000000..c231ece --- /dev/null +++ b/src/core/srcbc/crypto/generators/DsaKeyPairGenerator.cs @@ -0,0 +1,56 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a DSA key pair generator. + * + * This Generates DSA keys in line with the method described + * in FIPS 186-2. + */ + public class DsaKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private DsaKeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + // Note: If we start accepting instances of KeyGenerationParameters, + // must apply constraint checking on strength (see DsaParametersGenerator.Init) + + this.param = (DsaKeyGenerationParameters) parameters; + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + DsaParameters dsaParams = param.Parameters; + SecureRandom random = param.Random; + + BigInteger q = dsaParams.Q; + BigInteger x; + + do + { + x = new BigInteger(160, random); + } + while (x.SignValue == 0 || x.CompareTo(q) >= 0); + + // + // calculate the public key. + // + BigInteger y = dsaParams.G.ModPow(x, dsaParams.P); + + return new AsymmetricCipherKeyPair( + new DsaPublicKeyParameters(y, dsaParams), + new DsaPrivateKeyParameters(x, dsaParams)); + } + } +} diff --git a/src/core/srcbc/crypto/generators/DsaParametersGenerator.cs b/src/core/srcbc/crypto/generators/DsaParametersGenerator.cs new file mode 100644 index 0000000..fbe8942 --- /dev/null +++ b/src/core/srcbc/crypto/generators/DsaParametersGenerator.cs @@ -0,0 +1,184 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generate suitable parameters for DSA, in line with FIPS 186-2. + */ + public class DsaParametersGenerator + { + private int size; + private int certainty; + private SecureRandom random; + + /** + * initialise the key generator. + * + * @param size size of the key (range 2^512 -> 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. + */ + public void Init( + int size, + int certainty, + SecureRandom random) + { + if (!IsValidDsaStrength(size)) + throw new ArgumentException("size must be from 512 - 1024 and a multiple of 64", "size"); + + this.size = size; + this.certainty = certainty; + this.random = random; + } + + /** + * add value to b, returning the result in a. The a value is treated + * as a BigInteger of length (a.Length * 8) bits. The result is + * modulo 2^a.Length in case of overflow. + */ + private static void Add( + byte[] a, + byte[] b, + int value) + { + int x = (b[b.Length - 1] & 0xff) + value; + + a[b.Length - 1] = (byte)x; + x = (int) ((uint) x >>8); + + for (int i = b.Length - 2; i >= 0; i--) + { + x += (b[i] & 0xff); + a[i] = (byte)x; + x = (int) ((uint) x >>8); + } + } + + /** + * which Generates the p and g values from the given parameters, + * returning the DsaParameters object. + *

+ * Note: can take a while...

+ */ + public DsaParameters GenerateParameters() + { + byte[] seed = new byte[20]; + byte[] part1 = new byte[20]; + byte[] part2 = new byte[20]; + byte[] u = new byte[20]; + Sha1Digest sha1 = new Sha1Digest(); + int n = (size - 1) / 160; + byte[] w = new byte[size / 8]; + + BigInteger q = null, p = null, g = null; + int counter = 0; + bool primesFound = false; + + while (!primesFound) + { + do + { + random.NextBytes(seed); + + sha1.BlockUpdate(seed, 0, seed.Length); + + sha1.DoFinal(part1, 0); + + Array.Copy(seed, 0, part2, 0, seed.Length); + + Add(part2, seed, 1); + + sha1.BlockUpdate(part2, 0, part2.Length); + + sha1.DoFinal(part2, 0); + + for (int i = 0; i != u.Length; i++) + { + u[i] = (byte)(part1[i] ^ part2[i]); + } + + u[0] |= (byte)0x80; + u[19] |= (byte)0x01; + + q = new BigInteger(1, u); + } + while (!q.IsProbablePrime(certainty)); + + counter = 0; + + int offset = 2; + + while (counter < 4096) + { + for (int k = 0; k < n; k++) + { + Add(part1, seed, offset + k); + sha1.BlockUpdate(part1, 0, part1.Length); + sha1.DoFinal(part1, 0); + Array.Copy(part1, 0, w, w.Length - (k + 1) * part1.Length, part1.Length); + } + + Add(part1, seed, offset + n); + sha1.BlockUpdate(part1, 0, part1.Length); + sha1.DoFinal(part1, 0); + Array.Copy(part1, part1.Length - ((w.Length - (n) * part1.Length)), w, 0, w.Length - n * part1.Length); + + w[0] |= (byte)0x80; + + BigInteger x = new BigInteger(1, w); + + BigInteger c = x.Mod(q.ShiftLeft(1)); + + p = x.Subtract(c.Subtract(BigInteger.One)); + + if (p.TestBit(size - 1)) + { + if (p.IsProbablePrime(certainty)) + { + primesFound = true; + break; + } + } + + counter += 1; + offset += n + 1; + } + } + + // + // calculate the generator g + // + BigInteger pMinusOneOverQ = p.Subtract(BigInteger.One).Divide(q); + + for (;;) + { + BigInteger h = new BigInteger(size, random); + if (h.CompareTo(BigInteger.One) <= 0 || h.CompareTo(p.Subtract(BigInteger.One)) >= 0) + { + continue; + } + + g = h.ModPow(pMinusOneOverQ, p); + if (g.CompareTo(BigInteger.One) <= 0) + { + continue; + } + + break; + } + + return new DsaParameters(p, q, g, new DsaValidationParameters(seed, counter)); + } + + private static bool IsValidDsaStrength( + int strength) + { + return strength >= 512 && strength <= 1024 && strength % 64 == 0; + } + } +} diff --git a/src/core/srcbc/crypto/generators/ECKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/ECKeyPairGenerator.cs new file mode 100644 index 0000000..7d7718c --- /dev/null +++ b/src/core/srcbc/crypto/generators/ECKeyPairGenerator.cs @@ -0,0 +1,130 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class ECKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private readonly string algorithm; + + private ECDomainParameters parameters; + private DerObjectIdentifier publicKeyParamSet; + private SecureRandom random; + + public ECKeyPairGenerator() + : this("EC") + { + } + + public ECKeyPairGenerator( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + this.algorithm = VerifyAlgorithmName(algorithm); + } + + public void Init( + KeyGenerationParameters parameters) + { + if (parameters is ECKeyGenerationParameters) + { + ECKeyGenerationParameters ecP = (ECKeyGenerationParameters) parameters; + + if (ecP.PublicKeyParamSet != null) + { + if (algorithm != "ECGOST3410") + throw new ArgumentException("parameters invalid for algorithm: " + algorithm, "parameters"); + + this.publicKeyParamSet = ecP.PublicKeyParamSet; + } + + this.parameters = ecP.DomainParameters; + } + else + { + DerObjectIdentifier oid; + switch (parameters.Strength) + { + case 192: + oid = X9ObjectIdentifiers.Prime192v1; + break; + case 239: + oid = X9ObjectIdentifiers.Prime239v1; + break; + case 256: + oid = X9ObjectIdentifiers.Prime256v1; + break; + default: + throw new InvalidParameterException("unknown key size."); + } + + X9ECParameters ecps = X962NamedCurves.GetByOid(oid); + + this.parameters = new ECDomainParameters( + ecps.Curve, ecps.G, ecps.N, ecps.H, ecps.GetSeed()); + } + + this.random = parameters.Random; + } + + /** + * Given the domain parameters this routine Generates an EC key + * pair in accordance with X9.62 section 5.2.1 pages 26, 27. + */ + public AsymmetricCipherKeyPair GenerateKeyPair() + { + BigInteger n = parameters.N; + BigInteger d; + + do + { + d = new BigInteger(n.BitLength, random); + } + while (d.SignValue == 0 || (d.CompareTo(n) >= 0)); + + ECPoint q = parameters.G.Multiply(d); + + if (publicKeyParamSet != null) + { + return new AsymmetricCipherKeyPair( + new ECPublicKeyParameters(q, publicKeyParamSet), + new ECPrivateKeyParameters(d, publicKeyParamSet)); + } + + return new AsymmetricCipherKeyPair( + new ECPublicKeyParameters(algorithm, q, parameters), + new ECPrivateKeyParameters(algorithm, d, parameters)); + } + + private string VerifyAlgorithmName( + string algorithm) + { + string upper = algorithm.ToUpper(CultureInfo.InvariantCulture); + + switch (upper) + { + case "EC": + case "ECDSA": + case "ECGOST3410": + case "ECDH": + case "ECDHC": + break; + default: + throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm"); + } + + return upper; + } + } +} diff --git a/src/core/srcbc/crypto/generators/ElGamalKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/ElGamalKeyPairGenerator.cs new file mode 100644 index 0000000..9e3f54d --- /dev/null +++ b/src/core/srcbc/crypto/generators/ElGamalKeyPairGenerator.cs @@ -0,0 +1,40 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a ElGamal key pair generator. + *

+ * This Generates keys consistent for use with ElGamal as described in + * page 164 of "Handbook of Applied Cryptography".

+ */ + public class ElGamalKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private ElGamalKeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + this.param = (ElGamalKeyGenerationParameters) parameters; + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance; + ElGamalParameters elParams = param.Parameters; + + BigInteger p = elParams.P; + BigInteger x = helper.CalculatePrivate(p, param.Random, elParams.L); + BigInteger y = helper.CalculatePublic(p, elParams.G, x); + + return new AsymmetricCipherKeyPair( + new ElGamalPublicKeyParameters(y, elParams), + new ElGamalPrivateKeyParameters(x, elParams)); + } + } + +} diff --git a/src/core/srcbc/crypto/generators/ElGamalParametersGenerator.cs b/src/core/srcbc/crypto/generators/ElGamalParametersGenerator.cs new file mode 100644 index 0000000..1bb9f4e --- /dev/null +++ b/src/core/srcbc/crypto/generators/ElGamalParametersGenerator.cs @@ -0,0 +1,46 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class ElGamalParametersGenerator + { + private int size; + private int certainty; + private SecureRandom random; + + 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 ElGamalParameters object. + *

+ * Note: can take a while... + *

+ */ + public ElGamalParameters GenerateParameters() + { + // + // find a safe prime p where p = 2*q + 1, where p and q are prime. + // + BigInteger[] safePrimes = DHParametersHelper.GenerateSafePrimes(size, certainty, random); + + BigInteger p = safePrimes[0]; + BigInteger q = safePrimes[1]; + BigInteger g = DHParametersHelper.SelectGenerator(p, q, random); + + return new ElGamalParameters(p, g); + } + } +} diff --git a/src/core/srcbc/crypto/generators/GOST3410KeyPairGenerator.cs b/src/core/srcbc/crypto/generators/GOST3410KeyPairGenerator.cs new file mode 100644 index 0000000..e8bc48d --- /dev/null +++ b/src/core/srcbc/crypto/generators/GOST3410KeyPairGenerator.cs @@ -0,0 +1,73 @@ +using System; + +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a GOST3410 key pair generator. + * This generates GOST3410 keys in line with the method described + * in GOST R 34.10-94. + */ + public class Gost3410KeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private Gost3410KeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + if (parameters is Gost3410KeyGenerationParameters) + { + this.param = (Gost3410KeyGenerationParameters) parameters; + } + else + { + Gost3410KeyGenerationParameters kgp = new Gost3410KeyGenerationParameters( + parameters.Random, + CryptoProObjectIdentifiers.GostR3410x94CryptoProA); + + if (parameters.Strength != kgp.Parameters.P.BitLength - 1) + { + // TODO Should we complain? + } + + this.param = kgp; + } + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + SecureRandom random = param.Random; + Gost3410Parameters gost3410Params = param.Parameters; + + BigInteger q = gost3410Params.Q; + BigInteger x; + do + { + x = new BigInteger(256, random); + } + while (x.SignValue < 1 || x.CompareTo(q) >= 0); + + BigInteger p = gost3410Params.P; + BigInteger a = gost3410Params.A; + + // calculate the public key. + BigInteger y = a.ModPow(x, p); + + if (param.PublicKeyParamSet != null) + { + return new AsymmetricCipherKeyPair( + new Gost3410PublicKeyParameters(y, param.PublicKeyParamSet), + new Gost3410PrivateKeyParameters(x, param.PublicKeyParamSet)); + } + + return new AsymmetricCipherKeyPair( + new Gost3410PublicKeyParameters(y, gost3410Params), + new Gost3410PrivateKeyParameters(x, gost3410Params)); + } + } +} diff --git a/src/core/srcbc/crypto/generators/GOST3410ParametersGenerator.cs b/src/core/srcbc/crypto/generators/GOST3410ParametersGenerator.cs new file mode 100644 index 0000000..01d4a63 --- /dev/null +++ b/src/core/srcbc/crypto/generators/GOST3410ParametersGenerator.cs @@ -0,0 +1,530 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * generate suitable parameters for GOST3410. + */ + public class Gost3410ParametersGenerator + { + private int size; + private int typeproc; + private SecureRandom init_random; + + /** + * initialise the key generator. + * + * @param size size of the key + * @param typeProcedure type procedure A,B = 1; A',B' - else + * @param random random byte source. + */ + public void Init( + int size, + int typeProcedure, + SecureRandom random) + { + this.size = size; + this.typeproc = typeProcedure; + this.init_random = random; + } + + //Procedure A + private int procedure_A(int x0, int c, BigInteger[] pq, int size) + { + //Verify and perform condition: 065536) + { + x0 = init_random.NextInt()/32768; + } + + while((c<0 || c>65536) || (c/2==0)) + { + c = init_random.NextInt()/32768 + 1; + } + + BigInteger C = BigInteger.ValueOf(c); + BigInteger constA16 = BigInteger.ValueOf(19381); + + //step1 + BigInteger[] y = new BigInteger[1]; // begin length = 1 + y[0] = BigInteger.ValueOf(x0); + + //step 2 + int[] t = new int[1]; // t - orders; begin length = 1 + t[0] = size; + int s = 0; + for (int i=0; t[i]>=17; i++) + { + // extension array t + int[] tmp_t = new int[t.Length + 1]; /////////////// + Array.Copy(t,0,tmp_t,0,t.Length); // extension + t = new int[tmp_t.Length]; // array t + Array.Copy(tmp_t, 0, t, 0, tmp_t.Length); /////////////// + + t[i+1] = t[i]/2; + s = i+1; + } + + //step3 + BigInteger[] p = new BigInteger[s+1]; + p[s] = new BigInteger("8003",16); //set min prime number length 16 bit + + int m = s-1; //step4 + + for (int i=0; i t[m]) + { + goto step6; //step 12 + } + + p[m] = NByLastP.Add(BigInteger.One); + + //step13 + if (BigInteger.Two.ModPow(NByLastP, p[m]).CompareTo(BigInteger.One) == 0 + && BigInteger.Two.ModPow(N, p[m]).CompareTo(BigInteger.One) != 0) + { + break; + } + + N = N.Add(BigInteger.Two); + } + + if (--m < 0) + { + pq[0] = p[0]; + pq[1] = p[1]; + return y[0].IntValue; //return for procedure B step 2 + } + + break; //step 14 + } + } + return y[0].IntValue; + } + + //Procedure A' + private long procedure_Aa(long x0, long c, BigInteger[] pq, int size) + { + //Verify and perform condition: 04294967296L) + { + x0 = init_random.NextInt()*2; + } + + while((c<0 || c>4294967296L) || (c/2==0)) + { + c = init_random.NextInt()*2+1; + } + + BigInteger C = BigInteger.ValueOf(c); + BigInteger constA32 = BigInteger.ValueOf(97781173); + + //step1 + BigInteger[] y = new BigInteger[1]; // begin length = 1 + y[0] = BigInteger.ValueOf(x0); + + //step 2 + int[] t = new int[1]; // t - orders; begin length = 1 + t[0] = size; + int s = 0; + for (int i=0; t[i]>=33; i++) + { + // extension array t + int[] tmp_t = new int[t.Length + 1]; /////////////// + Array.Copy(t,0,tmp_t,0,t.Length); // extension + t = new int[tmp_t.Length]; // array t + Array.Copy(tmp_t, 0, t, 0, tmp_t.Length); /////////////// + + t[i+1] = t[i]/2; + s = i+1; + } + + //step3 + BigInteger[] p = new BigInteger[s+1]; + p[s] = new BigInteger("8000000B",16); //set min prime number length 32 bit + + int m = s-1; //step4 + + for (int i=0; i t[m]) + { + goto step6; //step 12 + } + + p[m] = NByLastP.Add(BigInteger.One); + + //step13 + if (BigInteger.Two.ModPow(NByLastP, p[m]).CompareTo(BigInteger.One) == 0 + && BigInteger.Two.ModPow(N, p[m]).CompareTo(BigInteger.One) != 0) + { + break; + } + + N = N.Add(BigInteger.Two); + } + + if (--m < 0) + { + pq[0] = p[0]; + pq[1] = p[1]; + return y[0].LongValue; //return for procedure B' step 2 + } + + break; //step 14 + } + } + return y[0].LongValue; + } + + //Procedure B + private void procedure_B(int x0, int c, BigInteger[] pq) + { + //Verify and perform condition: 065536) + { + x0 = init_random.NextInt()/32768; + } + + while((c<0 || c>65536) || (c/2==0)) + { + c = init_random.NextInt()/32768 + 1; + } + + BigInteger [] qp = new BigInteger[2]; + BigInteger q = null, Q = null, p = null; + BigInteger C = BigInteger.ValueOf(c); + BigInteger constA16 = BigInteger.ValueOf(19381); + + //step1 + x0 = procedure_A(x0, c, qp, 256); + q = qp[0]; + + //step2 + x0 = procedure_A(x0, c, qp, 512); + Q = qp[0]; + + BigInteger[] y = new BigInteger[65]; + y[0] = BigInteger.ValueOf(x0); + + const int tp = 1024; + + BigInteger qQ = q.Multiply(Q); + +step3: + for(;;) + { + //step 3 + for (int j=0; j<64; j++) + { + y[j+1] = (y[j].Multiply(constA16).Add(C)).Mod(BigInteger.Two.Pow(16)); + } + + //step 4 + BigInteger Y = BigInteger.Zero; + + for (int j=0; j<64; j++) + { + Y = Y.Add(y[j].ShiftLeft(16*j)); + } + + y[0] = y[64]; //step 5 + + //step 6 + BigInteger N = BigInteger.One.ShiftLeft(tp-1).Divide(qQ).Add( + Y.ShiftLeft(tp-1).Divide(qQ.ShiftLeft(1024))); + + if (N.TestBit(0)) + { + N = N.Add(BigInteger.One); + } + + //step 7 + + for(;;) + { + //step 11 + BigInteger qQN = qQ.Multiply(N); + + if (qQN.BitLength > tp) + { + goto step3; //step 9 + } + + p = qQN.Add(BigInteger.One); + + //step10 + if (BigInteger.Two.ModPow(qQN, p).CompareTo(BigInteger.One) == 0 + && BigInteger.Two.ModPow(q.Multiply(N), p).CompareTo(BigInteger.One) != 0) + { + pq[0] = p; + pq[1] = q; + return; + } + + N = N.Add(BigInteger.Two); + } + } + } + + //Procedure B' + private void procedure_Bb(long x0, long c, BigInteger[] pq) + { + //Verify and perform condition: 04294967296L) + { + x0 = init_random.NextInt()*2; + } + + while((c<0 || c>4294967296L) || (c/2==0)) + { + c = init_random.NextInt()*2+1; + } + + BigInteger [] qp = new BigInteger[2]; + BigInteger q = null, Q = null, p = null; + BigInteger C = BigInteger.ValueOf(c); + BigInteger constA32 = BigInteger.ValueOf(97781173); + + //step1 + x0 = procedure_Aa(x0, c, qp, 256); + q = qp[0]; + + //step2 + x0 = procedure_Aa(x0, c, qp, 512); + Q = qp[0]; + + BigInteger[] y = new BigInteger[33]; + y[0] = BigInteger.ValueOf(x0); + + const int tp = 1024; + + BigInteger qQ = q.Multiply(Q); + +step3: + for(;;) + { + //step 3 + for (int j=0; j<32; j++) + { + y[j+1] = (y[j].Multiply(constA32).Add(C)).Mod(BigInteger.Two.Pow(32)); + } + + //step 4 + BigInteger Y = BigInteger.Zero; + for (int j=0; j<32; j++) + { + Y = Y.Add(y[j].ShiftLeft(32*j)); + } + + y[0] = y[32]; //step 5 + + //step 6 + BigInteger N = BigInteger.One.ShiftLeft(tp-1).Divide(qQ).Add( + Y.ShiftLeft(tp-1).Divide(qQ.ShiftLeft(1024))); + + if (N.TestBit(0)) + { + N = N.Add(BigInteger.One); + } + + //step 7 + + for(;;) + { + //step 11 + BigInteger qQN = qQ.Multiply(N); + + if (qQN.BitLength > tp) + { + goto step3; //step 9 + } + + p = qQN.Add(BigInteger.One); + + //step10 + if (BigInteger.Two.ModPow(qQN, p).CompareTo(BigInteger.One) == 0 + && BigInteger.Two.ModPow(q.Multiply(N), p).CompareTo(BigInteger.One) != 0) + { + pq[0] = p; + pq[1] = q; + return; + } + + N = N.Add(BigInteger.Two); + } + } + } + + + /** + * Procedure C + * procedure generates the a value from the given p,q, + * returning the a value. + */ + private BigInteger procedure_C(BigInteger p, BigInteger q) + { + BigInteger pSub1 = p.Subtract(BigInteger.One); + BigInteger pSub1Divq = pSub1.Divide(q); + + for(;;) + { + BigInteger d = new BigInteger(p.BitLength, init_random); + + // 1 < d < p-1 + if (d.CompareTo(BigInteger.One) > 0 && d.CompareTo(pSub1) < 0) + { + BigInteger a = d.ModPow(pSub1Divq, p); + + if (a.CompareTo(BigInteger.One) != 0) + { + return a; + } + } + } + } + + /** + * which generates the p , q and a values from the given parameters, + * returning the Gost3410Parameters object. + */ + public Gost3410Parameters GenerateParameters() + { + BigInteger [] pq = new BigInteger[2]; + BigInteger q = null, p = null, a = null; + + int x0, c; + long x0L, cL; + + if (typeproc==1) + { + x0 = init_random.NextInt(); + c = init_random.NextInt(); + + switch(size) + { + case 512: + procedure_A(x0, c, pq, 512); + break; + case 1024: + procedure_B(x0, c, pq); + break; + default: + throw new ArgumentException("Ooops! key size 512 or 1024 bit."); + } + p = pq[0]; q = pq[1]; + a = procedure_C(p, q); + //System.out.println("p:"+p.toString(16)+"\n"+"q:"+q.toString(16)+"\n"+"a:"+a.toString(16)); + //System.out.println("p:"+p+"\n"+"q:"+q+"\n"+"a:"+a); + return new Gost3410Parameters(p, q, a, new Gost3410ValidationParameters(x0, c)); + } + else + { + x0L = init_random.NextLong(); + cL = init_random.NextLong(); + + switch(size) + { + case 512: + procedure_Aa(x0L, cL, pq, 512); + break; + case 1024: + procedure_Bb(x0L, cL, pq); + break; + default: + throw new InvalidOperationException("Ooops! key size 512 or 1024 bit."); + } + p = pq[0]; q = pq[1]; + a = procedure_C(p, q); + //System.out.println("p:"+p.toString(16)+"\n"+"q:"+q.toString(16)+"\n"+"a:"+a.toString(16)); + //System.out.println("p:"+p+"\n"+"q:"+q+"\n"+"a:"+a); + return new Gost3410Parameters(p, q, a, new Gost3410ValidationParameters(x0L, cL)); + } + } + } +} diff --git a/src/core/srcbc/crypto/generators/Kdf1BytesGenerator.cs b/src/core/srcbc/crypto/generators/Kdf1BytesGenerator.cs new file mode 100644 index 0000000..702e1f8 --- /dev/null +++ b/src/core/srcbc/crypto/generators/Kdf1BytesGenerator.cs @@ -0,0 +1,27 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * KFD2 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033 + *
+ * This implementation is based on IEEE P1363/ISO 18033. + */ + public class Kdf1BytesGenerator + : BaseKdfBytesGenerator + { + /** + * Construct a KDF1 byte generator. + * + * @param digest the digest to be used as the source of derived keys. + */ + public Kdf1BytesGenerator( + IDigest digest) + : base(0, digest) + { + } + } +} diff --git a/src/core/srcbc/crypto/generators/Kdf2BytesGenerator.cs b/src/core/srcbc/crypto/generators/Kdf2BytesGenerator.cs new file mode 100644 index 0000000..6ed78ce --- /dev/null +++ b/src/core/srcbc/crypto/generators/Kdf2BytesGenerator.cs @@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * KFD2 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033 + *
+ * This implementation is based on IEEE P1363/ISO 18033. + */ + public class Kdf2BytesGenerator + : BaseKdfBytesGenerator + { + /** + * Construct a KDF2 bytes generator. Generates key material + * according to IEEE P1363 or ISO 18033 depending on the initialisation. + * + * @param digest the digest to be used as the source of derived keys. + */ + public Kdf2BytesGenerator( + IDigest digest) + : base(1, digest) + { + } + } +} diff --git a/src/core/srcbc/crypto/generators/Mgf1BytesGenerator.cs b/src/core/srcbc/crypto/generators/Mgf1BytesGenerator.cs new file mode 100644 index 0000000..4c993a6 --- /dev/null +++ b/src/core/srcbc/crypto/generators/Mgf1BytesGenerator.cs @@ -0,0 +1,117 @@ +using System; +//using Org.BouncyCastle.Math; +//using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generator for MGF1 as defined in Pkcs 1v2 + */ + public class Mgf1BytesGenerator : IDerivationFunction + { + private IDigest digest; + private byte[] seed; + private int hLen; + + /** + * @param digest the digest to be used as the source of Generated bytes + */ + public Mgf1BytesGenerator( + IDigest digest) + { + this.digest = digest; + this.hLen = digest.GetDigestSize(); + } + + public void Init( + IDerivationParameters parameters) + { + if (!(typeof(MgfParameters).IsInstanceOfType(parameters))) + { + throw new ArgumentException("MGF parameters required for MGF1Generator"); + } + + MgfParameters p = (MgfParameters)parameters; + + seed = p.GetSeed(); + } + + /** + * return the underlying digest. + */ + public IDigest Digest + { + get + { + return digest; + } + } + + /** + * int to octet string. + */ + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint) i >> 24); + sp[1] = (byte)((uint) i >> 16); + sp[2] = (byte)((uint) i >> 8); + sp[3] = (byte)((uint) i >> 0); + } + + /** + * fill len bytes of the output buffer with bytes Generated from + * the derivation function. + * + * @throws DataLengthException if the out buffer is too small. + */ + public int GenerateBytes( + byte[] output, + int outOff, + int length) + { + if ((output.Length - length) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + byte[] hashBuf = new byte[hLen]; + byte[] C = new byte[4]; + int counter = 0; + + digest.Reset(); + + if (length > hLen) + { + do + { + ItoOSP(counter, C); + + digest.BlockUpdate(seed, 0, seed.Length); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, output, outOff + counter * hLen, hLen); + } + while (++counter < (length / hLen)); + } + + if ((counter * hLen) < length) + { + ItoOSP(counter, C); + + digest.BlockUpdate(seed, 0, seed.Length); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, output, outOff + counter * hLen, length - (counter * hLen)); + } + + return length; + } + } + +} diff --git a/src/core/srcbc/crypto/generators/NaccacheSternKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/NaccacheSternKeyPairGenerator.cs new file mode 100644 index 0000000..ad92230 --- /dev/null +++ b/src/core/srcbc/crypto/generators/NaccacheSternKeyPairGenerator.cs @@ -0,0 +1,330 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Key generation parameters for NaccacheStern cipher. For details on this cipher, please see + * + * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf + */ + public class NaccacheSternKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private static readonly int[] smallPrimes = + { + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, + 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, + 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, + 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, + 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, + 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, + 541, 547, 557 + }; + + private NaccacheSternKeyGenerationParameters param; + + /* + * (non-Javadoc) + * + * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#init(org.bouncycastle.crypto.KeyGenerationParameters) + */ + public void Init(KeyGenerationParameters parameters) + { + this.param = (NaccacheSternKeyGenerationParameters)parameters; + } + + /* + * (non-Javadoc) + * + * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#generateKeyPair() + */ + public AsymmetricCipherKeyPair GenerateKeyPair() + { + int strength = param.Strength; + SecureRandom rand = param.Random; + int certainty = param.Certainty; + bool debug = param.IsDebug; + + if (debug) + { + Console.WriteLine("Fetching first " + param.CountSmallPrimes + " primes."); + } + + ArrayList smallPrimes = findFirstPrimes(param.CountSmallPrimes); + + smallPrimes = permuteList(smallPrimes, rand); + + BigInteger u = BigInteger.One; + BigInteger v = BigInteger.One; + + for (int i = 0; i < smallPrimes.Count / 2; i++) + { + u = u.Multiply((BigInteger)smallPrimes[i]); + } + for (int i = smallPrimes.Count / 2; i < smallPrimes.Count; i++) + { + v = v.Multiply((BigInteger)smallPrimes[i]); + } + + BigInteger sigma = u.Multiply(v); + + // n = (2 a u p_ + 1 ) ( 2 b v q_ + 1) + // -> |n| = strength + // |2| = 1 in bits + // -> |a| * |b| = |n| - |u| - |v| - |p_| - |q_| - |2| -|2| + // remainingStrength = strength - sigma.bitLength() - p_.bitLength() - + // q_.bitLength() - 1 -1 + int remainingStrength = strength - sigma.BitLength - 48; + BigInteger a = generatePrime(remainingStrength / 2 + 1, certainty, rand); + BigInteger b = generatePrime(remainingStrength / 2 + 1, certainty, rand); + + BigInteger p_; + BigInteger q_; + BigInteger p; + BigInteger q; + + long tries = 0; + if (debug) + { + Console.WriteLine("generating p and q"); + } + + BigInteger _2au = a.Multiply(u).ShiftLeft(1); + BigInteger _2bv = b.Multiply(v).ShiftLeft(1); + + for (;;) + { + tries++; + + p_ = generatePrime(24, certainty, rand); + + p = p_.Multiply(_2au).Add(BigInteger.One); + + if (!p.IsProbablePrime(certainty)) + continue; + + for (;;) + { + q_ = generatePrime(24, certainty, rand); + + if (p_.Equals(q_)) + continue; + + q = q_.Multiply(_2bv).Add(BigInteger.One); + + if (q.IsProbablePrime(certainty)) + break; + } + + if (!sigma.Gcd(p_.Multiply(q_)).Equals(BigInteger.One)) + { + Console.WriteLine("sigma.gcd(p_.mult(q_)) != 1!\n p_: " + p_ +"\n q_: "+ q_ ); + continue; + } + + if (p.Multiply(q).BitLength < strength) + { + if (debug) + { + Console.WriteLine("key size too small. Should be " + strength + " but is actually " + + p.Multiply(q).BitLength); + } + continue; + } + break; + } + + if (debug) + { + Console.WriteLine("needed " + tries + " tries to generate p and q."); + } + + BigInteger n = p.Multiply(q); + BigInteger phi_n = p.Subtract(BigInteger.One).Multiply(q.Subtract(BigInteger.One)); + BigInteger g; + tries = 0; + if (debug) + { + Console.WriteLine("generating g"); + } + for (;;) + { + // TODO After the first loop, just regenerate one randomly-selected gPart each time? + ArrayList gParts = new ArrayList(); + for (int ind = 0; ind != smallPrimes.Count; ind++) + { + BigInteger i = (BigInteger)smallPrimes[ind]; + BigInteger e = phi_n.Divide(i); + + for (;;) + { + tries++; + + g = generatePrime(strength, certainty, rand); + + if (!g.ModPow(e, n).Equals(BigInteger.One)) + { + gParts.Add(g); + break; + } + } + } + g = BigInteger.One; + for (int i = 0; i < smallPrimes.Count; i++) + { + BigInteger gPart = (BigInteger) gParts[i]; + BigInteger smallPrime = (BigInteger) smallPrimes[i]; + g = g.Multiply(gPart.ModPow(sigma.Divide(smallPrime), n)).Mod(n); + } + + // make sure that g is not divisible by p_i or q_i + bool divisible = false; + for (int i = 0; i < smallPrimes.Count; i++) + { + if (g.ModPow(phi_n.Divide((BigInteger)smallPrimes[i]), n).Equals(BigInteger.One)) + { + if (debug) + { + Console.WriteLine("g has order phi(n)/" + smallPrimes[i] + "\n g: " + g); + } + divisible = true; + break; + } + } + + if (divisible) + { + continue; + } + + // make sure that g has order > phi_n/4 + + //if (g.ModPow(phi_n.Divide(BigInteger.ValueOf(4)), n).Equals(BigInteger.One)) + if (g.ModPow(phi_n.ShiftRight(2), n).Equals(BigInteger.One)) + { + if (debug) + { + Console.WriteLine("g has order phi(n)/4\n g:" + g); + } + continue; + } + + if (g.ModPow(phi_n.Divide(p_), n).Equals(BigInteger.One)) + { + if (debug) + { + Console.WriteLine("g has order phi(n)/p'\n g: " + g); + } + continue; + } + if (g.ModPow(phi_n.Divide(q_), n).Equals(BigInteger.One)) + { + if (debug) + { + Console.WriteLine("g has order phi(n)/q'\n g: " + g); + } + continue; + } + if (g.ModPow(phi_n.Divide(a), n).Equals(BigInteger.One)) + { + if (debug) + { + Console.WriteLine("g has order phi(n)/a\n g: " + g); + } + continue; + } + if (g.ModPow(phi_n.Divide(b), n).Equals(BigInteger.One)) + { + if (debug) + { + Console.WriteLine("g has order phi(n)/b\n g: " + g); + } + continue; + } + break; + } + if (debug) + { + Console.WriteLine("needed " + tries + " tries to generate g"); + Console.WriteLine(); + Console.WriteLine("found new NaccacheStern cipher variables:"); + Console.WriteLine("smallPrimes: " + Arrays.ToString(smallPrimes.ToArray())); + Console.WriteLine("sigma:...... " + sigma + " (" + sigma.BitLength + " bits)"); + Console.WriteLine("a:.......... " + a); + Console.WriteLine("b:.......... " + b); + Console.WriteLine("p':......... " + p_); + Console.WriteLine("q':......... " + q_); + Console.WriteLine("p:.......... " + p); + Console.WriteLine("q:.......... " + q); + Console.WriteLine("n:.......... " + n); + Console.WriteLine("phi(n):..... " + phi_n); + Console.WriteLine("g:.......... " + g); + Console.WriteLine(); + } + + return new AsymmetricCipherKeyPair(new NaccacheSternKeyParameters(false, g, n, sigma.BitLength), + new NaccacheSternPrivateKeyParameters(g, n, sigma.BitLength, smallPrimes, phi_n)); + } + + private static BigInteger generatePrime( + int bitLength, + int certainty, + SecureRandom rand) + { + return new BigInteger(bitLength, certainty, rand); + } + + /** + * Generates a permuted ArrayList from the original one. The original List + * is not modified + * + * @param arr + * the ArrayList to be permuted + * @param rand + * the source of Randomness for permutation + * @return a new ArrayList with the permuted elements. + */ + private static ArrayList permuteList( + ArrayList arr, + SecureRandom rand) + { + ArrayList retval = new ArrayList(arr.Count); + + foreach (object element in arr) + { + int index = rand.Next(retval.Count + 1); + retval.Insert(index, element); + } + + return retval; + } + + /** + * Finds the first 'count' primes starting with 3 + * + * @param count + * the number of primes to find + * @return a vector containing the found primes as Integer + */ + private static ArrayList findFirstPrimes( + int count) + { + ArrayList primes = new ArrayList(count); + + for (int i = 0; i != count; i++) + { + primes.Add(BigInteger.ValueOf(smallPrimes[i])); + } + + return primes; + } + + } +} diff --git a/src/core/srcbc/crypto/generators/OpenSSLPBEParametersGenerator.cs b/src/core/srcbc/crypto/generators/OpenSSLPBEParametersGenerator.cs new file mode 100644 index 0000000..fd47f0e --- /dev/null +++ b/src/core/srcbc/crypto/generators/OpenSSLPBEParametersGenerator.cs @@ -0,0 +1,167 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generator for PBE derived keys and ivs as usd by OpenSSL. + *

+ * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an + * iteration count of 1. + *

+ */ + public class OpenSslPbeParametersGenerator + : PbeParametersGenerator + { + private readonly IDigest digest = new MD5Digest(); + + /** + * Construct a OpenSSL Parameters generator. + */ + public OpenSslPbeParametersGenerator() + { + } + + public override void Init( + byte[] password, + byte[] salt, + int iterationCount) + { + // Ignore the provided iterationCount + base.Init(password, salt, 1); + } + + /** + * Initialise - note the iteration count for this algorithm is fixed at 1. + * + * @param password password to use. + * @param salt salt to use. + */ + public virtual void Init( + byte[] password, + byte[] salt) + { + base.Init(password, salt, 1); + } + + /** + * the derived key function, the ith hash of the password and the salt. + */ + private byte[] GenerateDerivedKey( + int bytesNeeded) + { + byte[] buf = new byte[digest.GetDigestSize()]; + byte[] key = new byte[bytesNeeded]; + int offset = 0; + + for (;;) + { + digest.BlockUpdate(mPassword, 0, mPassword.Length); + digest.BlockUpdate(mSalt, 0, mSalt.Length); + + digest.DoFinal(buf, 0); + + int len = (bytesNeeded > buf.Length) ? buf.Length : bytesNeeded; + Array.Copy(buf, 0, key, offset, len); + offset += len; + + // check if we need any more + bytesNeeded -= len; + if (bytesNeeded == 0) + { + break; + } + + // do another round + digest.Reset(); + digest.BlockUpdate(buf, 0, buf.Length); + } + + return key; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception ArgumentException if the key length larger than the base hash size. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize) + { + return GenerateDerivedMacParameters(keySize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize); + + return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + * @exception ArgumentException if keySize + ivSize is larger than the base hash size. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize, + int ivSize) + { + keySize = keySize / 8; + ivSize = ivSize / 8; + + byte[] dKey = GenerateDerivedKey(keySize + ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize + ivSize); + KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + + return new ParametersWithIV(key, dKey, keySize, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception ArgumentException if the key length larger than the base hash size. + */ + public override ICipherParameters GenerateDerivedMacParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = GenerateDerivedKey(keySize); + + return new KeyParameter(dKey, 0, keySize); + } + } +} diff --git a/src/core/srcbc/crypto/generators/Pkcs12ParametersGenerator.cs b/src/core/srcbc/crypto/generators/Pkcs12ParametersGenerator.cs new file mode 100644 index 0000000..9fc1f67 --- /dev/null +++ b/src/core/srcbc/crypto/generators/Pkcs12ParametersGenerator.cs @@ -0,0 +1,245 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generator for Pbe derived keys and ivs as defined by Pkcs 12 V1.0. + *

+ * The document this implementation is based on can be found at + * + * RSA's Pkcs12 Page + *

+ */ + public class Pkcs12ParametersGenerator + : PbeParametersGenerator + { + public const int KeyMaterial = 1; + public const int IVMaterial = 2; + public const int MacMaterial = 3; + + private readonly IDigest digest; + + private readonly int u; + private readonly int v; + + /** + * Construct a Pkcs 12 Parameters generator. + * + * @param digest the digest to be used as the source of derived keys. + * @exception ArgumentException if an unknown digest is passed in. + */ + public Pkcs12ParametersGenerator( + IDigest digest) + { + this.digest = digest; + + u = digest.GetDigestSize(); + v = digest.GetByteLength(); + } + + /** + * add a + b + 1, returning the result in a. The a value is treated + * as a BigInteger of length (b.Length * 8) bits. The result is + * modulo 2^b.Length in case of overflow. + */ + private void Adjust( + byte[] a, + int aOff, + byte[] b) + { + int x = (b[b.Length - 1] & 0xff) + (a[aOff + b.Length - 1] & 0xff) + 1; + + a[aOff + b.Length - 1] = (byte)x; + x = (int) ((uint) x >> 8); + + for (int i = b.Length - 2; i >= 0; i--) + { + x += (b[i] & 0xff) + (a[aOff + i] & 0xff); + a[aOff + i] = (byte)x; + x = (int) ((uint) x >> 8); + } + } + + /** + * generation of a derived key ala Pkcs12 V1.0. + */ + private byte[] GenerateDerivedKey( + int idByte, + int n) + { + byte[] D = new byte[v]; + byte[] dKey = new byte[n]; + + for (int i = 0; i != D.Length; i++) + { + D[i] = (byte)idByte; + } + + byte[] S; + + if ((mSalt != null) && (mSalt.Length != 0)) + { + S = new byte[v * ((mSalt.Length + v - 1) / v)]; + + for (int i = 0; i != S.Length; i++) + { + S[i] = mSalt[i % mSalt.Length]; + } + } + else + { + S = new byte[0]; + } + + byte[] P; + + if ((mPassword != null) && (mPassword.Length != 0)) + { + P = new byte[v * ((mPassword.Length + v - 1) / v)]; + + for (int i = 0; i != P.Length; i++) + { + P[i] = mPassword[i % mPassword.Length]; + } + } + else + { + P = new byte[0]; + } + + byte[] I = new byte[S.Length + P.Length]; + + Array.Copy(S, 0, I, 0, S.Length); + Array.Copy(P, 0, I, S.Length, P.Length); + + byte[] B = new byte[v]; + int c = (n + u - 1) / u; + + for (int i = 1; i <= c; i++) + { + byte[] A = new byte[u]; + + digest.BlockUpdate(D, 0, D.Length); + digest.BlockUpdate(I, 0, I.Length); + digest.DoFinal(A, 0); + for (int j = 1; j != mIterationCount; j++) + { + digest.BlockUpdate(A, 0, A.Length); + digest.DoFinal(A, 0); + } + + for (int j = 0; j != B.Length; j++) + { + B[j] = A[j % A.Length]; + } + + for (int j = 0; j != I.Length / v; j++) + { + Adjust(I, j * v, B); + } + + if (i == c) + { + Array.Copy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u)); + } + else + { + Array.Copy(A, 0, dKey, (i - 1) * u, A.Length); + } + } + + return dKey; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize); + + return new KeyParameter(dKey, 0, keySize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize); + + return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize); + + byte[] iv = GenerateDerivedKey(IVMaterial, ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize); + KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + + byte[] iv = GenerateDerivedKey(IVMaterial, ivSize); + + return new ParametersWithIV(key, iv, 0, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public override ICipherParameters GenerateDerivedMacParameters( + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(MacMaterial, keySize); + + return new KeyParameter(dKey, 0, keySize); + } + } +} diff --git a/src/core/srcbc/crypto/generators/Pkcs5S1ParametersGenerator.cs b/src/core/srcbc/crypto/generators/Pkcs5S1ParametersGenerator.cs new file mode 100644 index 0000000..5fa21f1 --- /dev/null +++ b/src/core/srcbc/crypto/generators/Pkcs5S1ParametersGenerator.cs @@ -0,0 +1,162 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generator for Pbe derived keys and ivs as defined by Pkcs 5 V2.0 Scheme 1. + * Note this generator is limited to the size of the hash produced by the + * digest used to drive it. + *

+ * The document this implementation is based on can be found at + * + * RSA's Pkcs5 Page + *

+ */ + public class Pkcs5S1ParametersGenerator + : PbeParametersGenerator + { + private readonly IDigest digest; + + /** + * Construct a Pkcs 5 Scheme 1 Parameters generator. + * + * @param digest the digest to be used as the source of derived keys. + */ + public Pkcs5S1ParametersGenerator( + IDigest digest) + { + this.digest = digest; + } + + /** + * the derived key function, the ith hash of the mPassword and the mSalt. + */ + private byte[] GenerateDerivedKey() + { + byte[] digestBytes = new byte[digest.GetDigestSize()]; + + digest.BlockUpdate(mPassword, 0, mPassword.Length); + digest.BlockUpdate(mSalt, 0, mSalt.Length); + + digest.DoFinal(digestBytes, 0); + for (int i = 1; i < mIterationCount; i++) + { + digest.BlockUpdate(digestBytes, 0, digestBytes.Length); + digest.DoFinal(digestBytes, 0); + } + + return digestBytes; + } + + /** + * Generate a key parameter derived from the mPassword, mSalt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception ArgumentException if the key length larger than the base hash size. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize) + { + return GenerateDerivedMacParameters(keySize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize) + { + keySize /= 8; + + if (keySize > digest.GetDigestSize()) + { + throw new ArgumentException( + "Can't Generate a derived key " + keySize + " bytes long."); + } + + byte[] dKey = GenerateDerivedKey(); + + return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the mPassword, mSalt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + * @exception ArgumentException if keySize + ivSize is larger than the base hash size. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + if ((keySize + ivSize) > digest.GetDigestSize()) + { + throw new ArgumentException( + "Can't Generate a derived key " + (keySize + ivSize) + " bytes long."); + } + + byte[] dKey = GenerateDerivedKey(); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + if ((keySize + ivSize) > digest.GetDigestSize()) + { + throw new ArgumentException( + "Can't Generate a derived key " + (keySize + ivSize) + " bytes long."); + } + + byte[] dKey = GenerateDerivedKey(); + KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + + return new ParametersWithIV(key, dKey, keySize, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the mPassword, + * mSalt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception ArgumentException if the key length larger than the base hash size. + */ + public override ICipherParameters GenerateDerivedMacParameters( + int keySize) + { + keySize /= 8; + + if (keySize > digest.GetDigestSize()) + { + throw new ArgumentException( + "Can't Generate a derived key " + keySize + " bytes long."); + } + + byte[] dKey = GenerateDerivedKey(); + + return new KeyParameter(dKey, 0, keySize); + } + } +} diff --git a/src/core/srcbc/crypto/generators/Pkcs5S2ParametersGenerator.cs b/src/core/srcbc/crypto/generators/Pkcs5S2ParametersGenerator.cs new file mode 100644 index 0000000..dbe0be0 --- /dev/null +++ b/src/core/srcbc/crypto/generators/Pkcs5S2ParametersGenerator.cs @@ -0,0 +1,175 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generator for Pbe derived keys and ivs as defined by Pkcs 5 V2.0 Scheme 2. + * This generator uses a SHA-1 HMac as the calculation function. + *

+ * The document this implementation is based on can be found at + * + * RSA's Pkcs5 Page

+ */ + public class Pkcs5S2ParametersGenerator + : PbeParametersGenerator + { + private readonly IMac hMac = new HMac(new Sha1Digest()); + + /** + * construct a Pkcs5 Scheme 2 Parameters generator. + */ + public Pkcs5S2ParametersGenerator() + { + } + + private void F( + byte[] P, + byte[] S, + int c, + byte[] iBuf, + byte[] outBytes, + int outOff) + { + byte[] state = new byte[hMac.GetMacSize()]; + ICipherParameters param = new KeyParameter(P); + + hMac.Init(param); + + if (S != null) + { + hMac.BlockUpdate(S, 0, S.Length); + } + + hMac.BlockUpdate(iBuf, 0, iBuf.Length); + + hMac.DoFinal(state, 0); + + Array.Copy(state, 0, outBytes, outOff, state.Length); + + for (int count = 1; count != c; count++) + { + hMac.Init(param); + hMac.BlockUpdate(state, 0, state.Length); + hMac.DoFinal(state, 0); + + for (int j = 0; j != state.Length; j++) + { + outBytes[outOff + j] ^= state[j]; + } + } + } + + private void IntToOctet( + byte[] Buffer, + int i) + { + Buffer[0] = (byte)((uint) i >> 24); + Buffer[1] = (byte)((uint) i >> 16); + Buffer[2] = (byte)((uint) i >> 8); + Buffer[3] = (byte)i; + } + + private byte[] GenerateDerivedKey( + int dkLen) + { + int hLen = hMac.GetMacSize(); + int l = (dkLen + hLen - 1) / hLen; + byte[] iBuf = new byte[4]; + byte[] outBytes = new byte[l * hLen]; + + for (int i = 1; i <= l; i++) + { + IntToOctet(iBuf, i); + + F(mPassword, mSalt, mIterationCount, iBuf, outBytes, (i - 1) * hLen); + } + + return outBytes; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize) + { + return GenerateDerivedMacParameters(keySize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize); + + return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize + ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize + ivSize); + KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + + return new ParametersWithIV(key, dKey, keySize, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public override ICipherParameters GenerateDerivedMacParameters( + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize); + + return new KeyParameter(dKey, 0, keySize); + } + } +} diff --git a/src/core/srcbc/crypto/generators/RSABlindingFactorGenerator.cs b/src/core/srcbc/crypto/generators/RSABlindingFactorGenerator.cs new file mode 100644 index 0000000..1583bd0 --- /dev/null +++ b/src/core/srcbc/crypto/generators/RSABlindingFactorGenerator.cs @@ -0,0 +1,69 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generate a random factor suitable for use with RSA blind signatures + * as outlined in Chaum's blinding and unblinding as outlined in + * "Handbook of Applied Cryptography", page 475. + */ + public class RsaBlindingFactorGenerator + { + private RsaKeyParameters key; + private SecureRandom random; + + /** + * Initialise the factor generator + * + * @param param the necessary RSA key parameters. + */ + public void Init( + ICipherParameters param) + { + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + key = (RsaKeyParameters)rParam.Parameters; + random = rParam.Random; + } + else + { + key = (RsaKeyParameters)param; + random = new SecureRandom(); + } + + if (key.IsPrivate) + throw new ArgumentException("generator requires RSA public key"); + } + + /** + * Generate a suitable blind factor for the public key the generator was initialised with. + * + * @return a random blind factor + */ + public BigInteger GenerateBlindingFactor() + { + if (key == null) + throw new InvalidOperationException("generator not initialised"); + + BigInteger m = key.Modulus; + int length = m.BitLength - 1; // must be less than m.BitLength + BigInteger factor; + BigInteger gcd; + + do + { + factor = new BigInteger(length, random); + gcd = factor.Gcd(m); + } + while (factor.SignValue == 0 || factor.Equals(BigInteger.One) || !gcd.Equals(BigInteger.One)); + + return factor; + } + } +} diff --git a/src/core/srcbc/crypto/generators/RsaKeyPairGenerator.cs b/src/core/srcbc/crypto/generators/RsaKeyPairGenerator.cs new file mode 100644 index 0000000..41fe687 --- /dev/null +++ b/src/core/srcbc/crypto/generators/RsaKeyPairGenerator.cs @@ -0,0 +1,139 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * an RSA key pair generator. + */ + public class RsaKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001); + private const int DefaultTests = 12; + + private RsaKeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + if (parameters is RsaKeyGenerationParameters) + { + this.param = (RsaKeyGenerationParameters)parameters; + } + else + { + this.param = new RsaKeyGenerationParameters( + DefaultPublicExponent, parameters.Random, parameters.Strength, DefaultTests); + } + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + BigInteger p, q, n, d, e, pSub1, qSub1, phi; + + // + // p and q values should have a length of half the strength in bits + // + int strength = param.Strength; + int pbitlength = (strength + 1) / 2; + int qbitlength = (strength - pbitlength); + int mindiffbits = strength / 3; + + e = param.PublicExponent; + + // 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 p, prime and (p-1) relatively prime to e + // + for (;;) + { + p = new BigInteger(pbitlength, 1, param.Random); + + if (p.Mod(e).Equals(BigInteger.One)) + continue; + + if (!p.IsProbablePrime(param.Certainty)) + continue; + + if (e.Gcd(p.Subtract(BigInteger.One)).Equals(BigInteger.One)) + break; + } + + // + // Generate a modulus of the required length + // + for (;;) + { + // Generate q, prime and (q-1) relatively prime to e, + // and not equal to p + // + for (;;) + { + q = new BigInteger(qbitlength, 1, param.Random); + + if (q.Subtract(p).Abs().BitLength < mindiffbits) + continue; + + if (q.Mod(e).Equals(BigInteger.One)) + continue; + + if (!q.IsProbablePrime(param.Certainty)) + continue; + + if (e.Gcd(q.Subtract(BigInteger.One)).Equals(BigInteger.One)) + break; + } + + // + // calculate the modulus + // + n = p.Multiply(q); + + if (n.BitLength == param.Strength) + break; + + // + // if we Get here our primes aren't big enough, make the largest + // of the two p and try again + // + p = p.Max(q); + } + + if (p.CompareTo(q) < 0) + { + phi = p; + p = q; + q = phi; + } + + pSub1 = p.Subtract(BigInteger.One); + qSub1 = q.Subtract(BigInteger.One); + phi = pSub1.Multiply(qSub1); + + // + // calculate the private exponent + // + d = e.ModInverse(phi); + + // + // calculate the CRT factors + // + BigInteger dP, dQ, qInv; + + dP = d.Remainder(pSub1); + dQ = d.Remainder(qSub1); + qInv = q.ModInverse(p); + + return new AsymmetricCipherKeyPair( + new RsaKeyParameters(false, n, e), + new RsaPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv)); + } + } + +} diff --git a/src/core/srcbc/crypto/io/CipherStream.cs b/src/core/srcbc/crypto/io/CipherStream.cs new file mode 100644 index 0000000..e69703c --- /dev/null +++ b/src/core/srcbc/crypto/io/CipherStream.cs @@ -0,0 +1,224 @@ +using System; +using System.Diagnostics; +using System.IO; +using Org.BouncyCastle.Crypto; +namespace Org.BouncyCastle.Crypto.IO +{ + public class CipherStream : Stream + { + internal Stream stream; + internal IBufferedCipher inCipher, outCipher; + private byte[] mInBuf; + private int mInPos; + private bool inStreamEnded; + + public CipherStream( + Stream stream, + IBufferedCipher readCipher, + IBufferedCipher writeCipher) + { + this.stream = stream; + + if (readCipher != null) + { + this.inCipher = readCipher; + mInBuf = null; + } + + if (writeCipher != null) + { + this.outCipher = writeCipher; + } + } + + public IBufferedCipher ReadCipher + { + get { return inCipher; } + } + + public IBufferedCipher WriteCipher + { + get { return outCipher; } + } + + public override int ReadByte() + { + if (inCipher == null) + { + return stream.ReadByte(); + } + + if (mInBuf == null || mInPos >= mInBuf.Length) + { + if (!FillInBuf()) + { + return -1; + } + } + + return mInBuf[mInPos++]; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (inCipher == null) + { + return stream.Read(buffer, offset, count); + } + +// int pos = offset; +// int end = offset + count; +// try +// { +// while (pos < end) +// { +// if (mInPos >= mInBufEnd && !FillInBuf()) break; +// +// int len = System.Math.Min(end - pos, mInBufEnd - mInPos); +// Array.Copy(mInBuf, mInPos, buffer, pos, len); +// mInPos += len; +// pos += len; +// } +// } +// catch (IOException) +// { +// if (pos == offset) throw; +// } +// return pos - offset; + + // TODO Optimise + int i = 0; + while (i < count) + { + int c = ReadByte(); + + if (c < 0) break; + + buffer[offset + i++] = (byte) c; + } + + return i; + } + private bool FillInBuf() + { + if (inStreamEnded) + { + return false; + } + + mInPos = 0; + + do + { + mInBuf = readAndProcessBlock(); + } + while (!inStreamEnded && mInBuf == null); + + return mInBuf != null; + } + private byte[] readAndProcessBlock() + { + int blockSize = inCipher.GetBlockSize(); + int readSize = (blockSize == 0) ? 256 : blockSize; + + byte[] block = new byte[readSize]; + int numRead = 0; + do + { + int count = stream.Read(block, numRead, block.Length - numRead); + if (count < 1) + { + inStreamEnded = true; + break; + } + numRead += count; + } + while (numRead < block.Length); + + Debug.Assert(inStreamEnded || numRead == block.Length); + + byte[] bytes = inStreamEnded + ? inCipher.DoFinal(block, 0, numRead) + : inCipher.ProcessBytes(block); + + if (bytes != null && bytes.Length == 0) + { + bytes = null; + } + + return bytes; + } + public override void Write(byte[] buffer, int offset, int count) + { + Debug.Assert(buffer != null); + Debug.Assert(0 <= offset && offset <= buffer.Length); + Debug.Assert(count >= 0); + + int end = offset + count; + + Debug.Assert(0 <= end && end <= buffer.Length); + + if (outCipher == null) + { + stream.Write(buffer, offset, count); + return; + } + + byte[] data = outCipher.ProcessBytes(buffer, offset, count); + if (data != null) + { + stream.Write(data, 0, data.Length); + } + } + public override void WriteByte( + byte value) + { + if (outCipher == null) + { + stream.WriteByte(value); + return; + } + + byte[] data = outCipher.ProcessByte(value); + if (data != null) + { + stream.Write(data, 0, data.Length); + } + } + public override bool CanRead + { + get { return stream.CanRead && (inCipher != null); } + } + public override bool CanWrite + { + get { return stream.CanWrite && (outCipher != null); } + } + public override bool CanSeek + { + get { return false; } + } + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + public override void Close() + { + if (outCipher != null) + { + byte[] data = outCipher.DoFinal(); + stream.Write(data, 0, data.Length); + stream.Flush(); + } + stream.Close(); + } + public override void Flush() + { + // Note: outCipher.DoFinal is only called during Close() + stream.Flush(); + } + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + } +} diff --git a/src/core/srcbc/crypto/io/DigestStream.cs b/src/core/srcbc/crypto/io/DigestStream.cs new file mode 100644 index 0000000..6a5ab42 --- /dev/null +++ b/src/core/srcbc/crypto/io/DigestStream.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using Org.BouncyCastle.Crypto; +namespace Org.BouncyCastle.Crypto.IO +{ + public class DigestStream : Stream + { + internal Stream stream; + internal IDigest inDigest; + internal IDigest outDigest; + + public DigestStream( + Stream stream, + IDigest readDigest, + IDigest writeDigest) + { + this.stream = stream; + this.inDigest = readDigest; + this.outDigest = writeDigest; + } + public IDigest ReadDigest() + { + return inDigest; + } + public IDigest WriteDigest() + { + return outDigest; + } + public override int ReadByte() + { + int b = stream.ReadByte(); + if (inDigest != null) + { + if (b >= 0) + { + inDigest.Update((byte)b); + } + } + return b; + } + + public override int Read(byte[] buffer, int offset, int count) + { + int n = stream.Read(buffer, offset, count); + if (inDigest != null) + { + if (n > 0) + { + inDigest.BlockUpdate(buffer, offset, n); + } + } + return n; + } + public override void Write( + byte[] buffer, + int offset, + int count) + { + if (outDigest != null) + { + if (count > 0) + { + outDigest.BlockUpdate(buffer, offset, count); + } + } + stream.Write(buffer, offset, count); + } + public override void WriteByte(byte value) + { + if (outDigest != null) + { + outDigest.Update(value); + } + stream.WriteByte(value); + } + public override bool CanRead + { + get { return stream.CanRead && (inDigest != null); } + } + public override bool CanWrite + { + get { return stream.CanWrite && (outDigest != null); } + } + public override bool CanSeek + { + get { return stream.CanSeek; } + } + public override long Length + { + get { return stream.Length; } + } + public override long Position + { + get { return stream.Position; } + set { stream.Position = value; } + } + public override void Close() + { + stream.Close(); + } + public override void Flush() + { + stream.Flush(); + } + public override long Seek( + long offset, + SeekOrigin origin) + { + return stream.Seek(offset,origin); + } + public override void SetLength(long value) + { + stream.SetLength(value); + } + } +} diff --git a/src/core/srcbc/crypto/io/MacStream.cs b/src/core/srcbc/crypto/io/MacStream.cs new file mode 100644 index 0000000..5f87b79 --- /dev/null +++ b/src/core/srcbc/crypto/io/MacStream.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using Org.BouncyCastle.Crypto; +namespace Org.BouncyCastle.Crypto.IO +{ + public class MacStream : Stream + { + internal Stream stream; + internal IMac inMac; + internal IMac outMac; + + public MacStream( + Stream stream, + IMac readMac, + IMac writeMac) + { + this.stream = stream; + this.inMac = readMac; + this.outMac = writeMac; + } + public IMac ReadMac() + { + return inMac; + } + public IMac WriteMac() + { + return outMac; + } + public override int ReadByte() + { + int b = stream.ReadByte(); + if (inMac != null) + { + if (b >= 0) + { + inMac.Update((byte)b); + } + } + return b; + } + + public override int Read(byte[] buffer, int offset, int count) + { + int n = stream.Read(buffer, offset, count); + if (inMac != null) + { + if (n > 0) + { + inMac.BlockUpdate(buffer, offset, count); + } + } + return n; + } + public override void Write( + byte[] buffer, + int offset, + int count) + { + if (outMac != null) + { + if (count > 0) + { + outMac.BlockUpdate(buffer, offset, count); + } + } + stream.Write(buffer, offset, count); + } + public override void WriteByte(byte value) + { + if (outMac != null) + { + outMac.Update(value); + } + stream.WriteByte(value); + } + public override bool CanRead + { + get { return stream.CanRead && (inMac != null); } + } + public override bool CanWrite + { + get { return stream.CanWrite && (outMac != null); } + } + public override bool CanSeek + { + get { return stream.CanSeek; } + } + public override long Length + { + get { return stream.Length; } + } + public override long Position + { + get { return stream.Position; } + set { stream.Position = value; } + } + public override void Close() + { + stream.Close(); + } + public override void Flush() + { + stream.Flush(); + } + public override long Seek( + long offset, + SeekOrigin origin) + { + return stream.Seek(offset,origin); + } + public override void SetLength(long value) + { + stream.SetLength(value); + } + } +} diff --git a/src/core/srcbc/crypto/macs/CMac.cs b/src/core/srcbc/crypto/macs/CMac.cs new file mode 100644 index 0000000..75b2920 --- /dev/null +++ b/src/core/srcbc/crypto/macs/CMac.cs @@ -0,0 +1,240 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html + *

+ * CMAC is analogous to OMAC1 - see also en.wikipedia.org/wiki/CMAC + *

+ * CMAC is a NIST recomendation - see + * csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf + *

+ * CMAC/OMAC1 is a blockcipher-based message authentication code designed and + * analyzed by Tetsu Iwata and Kaoru Kurosawa. + *

+ * CMAC/OMAC1 is a simple variant of the CBC MAC (Cipher Block Chaining Message + * Authentication Code). OMAC stands for One-Key CBC MAC. + *

+ * It supports 128- or 64-bits block ciphers, with any key size, and returns + * a MAC with dimension less or equal to the block size of the underlying + * cipher. + *

+ */ + public class CMac + : IMac + { + private const byte CONSTANT_128 = (byte)0x87; + private const byte CONSTANT_64 = (byte)0x1b; + + private byte[] ZEROES; + + private byte[] mac; + + private byte[] buf; + private int bufOff; + private IBlockCipher cipher; + + private int macSize; + + private byte[] L, Lu, Lu2; + + /** + * create a standard MAC based on a CBC block cipher (64 or 128 bit block). + * This will produce an authentication code the length of the block size + * of the cipher. + * + * @param cipher the cipher to be used as the basis of the MAC generation. + */ + public CMac( + IBlockCipher cipher) + : this(cipher, cipher.GetBlockSize() * 8) + { + } + + /** + * create a standard MAC based on a block cipher with the size of the + * MAC been given in bits. + *

+ * 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 @lt;= 128. + */ + public CMac( + IBlockCipher cipher, + int macSizeInBits) + { + if ((macSizeInBits % 8) != 0) + throw new ArgumentException("MAC size must be multiple of 8"); + + if (macSizeInBits > (cipher.GetBlockSize() * 8)) + { + throw new ArgumentException( + "MAC size must be less or equal to " + + (cipher.GetBlockSize() * 8)); + } + + if (cipher.GetBlockSize() != 8 && cipher.GetBlockSize() != 16) + { + throw new ArgumentException( + "Block size must be either 64 or 128 bits"); + } + + this.cipher = new CbcBlockCipher(cipher); + this.macSize = macSizeInBits / 8; + + mac = new byte[cipher.GetBlockSize()]; + + buf = new byte[cipher.GetBlockSize()]; + + ZEROES = new byte[cipher.GetBlockSize()]; + + bufOff = 0; + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + private byte[] doubleLu( + byte[] inBytes) + { + int FirstBit = (inBytes[0] & 0xFF) >> 7; + byte[] ret = new byte[inBytes.Length]; + for (int i = 0; i < inBytes.Length - 1; i++) + { + ret[i] = (byte)((inBytes[i] << 1) + ((inBytes[i + 1] & 0xFF) >> 7)); + } + ret[inBytes.Length - 1] = (byte)(inBytes[inBytes.Length - 1] << 1); + if (FirstBit == 1) + { + ret[inBytes.Length - 1] ^= inBytes.Length == 16 ? CONSTANT_128 : CONSTANT_64; + } + return ret; + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + + cipher.Init(true, parameters); + + //initializes the L, Lu, Lu2 numbers + L = new byte[ZEROES.Length]; + cipher.ProcessBlock(ZEROES, 0, L, 0); + Lu = doubleLu(L); + Lu2 = doubleLu(Lu); + + cipher.Init(true, parameters); + } + + public int GetMacSize() + { + return macSize; + } + + public void Update( + byte input) + { + if (bufOff == buf.Length) + { + cipher.ProcessBlock(buf, 0, mac, 0); + bufOff = 0; + } + + buf[bufOff++] = input; + } + + public void BlockUpdate( + byte[] inBytes, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int blockSize = cipher.GetBlockSize(); + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(inBytes, inOff, buf, bufOff, gapLen); + + cipher.ProcessBlock(buf, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + cipher.ProcessBlock(inBytes, inOff, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(inBytes, inOff, buf, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] outBytes, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + + byte[] lu; + if (bufOff == blockSize) + { + lu = Lu; + } + else + { + new ISO7816d4Padding().AddPadding(buf, bufOff); + lu = Lu2; + } + + for (int i = 0; i < mac.Length; i++) + { + buf[i] ^= lu[i]; + } + + cipher.ProcessBlock(buf, 0, mac, 0); + + Array.Copy(mac, 0, outBytes, outOff, macSize); + + Reset(); + + return macSize; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + /* + * clean the buffer. + */ + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + /* + * Reset the underlying cipher. + */ + cipher.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/macs/CbcBlockCipherMac.cs b/src/core/srcbc/crypto/macs/CbcBlockCipherMac.cs new file mode 100644 index 0000000..95e8573 --- /dev/null +++ b/src/core/srcbc/crypto/macs/CbcBlockCipherMac.cs @@ -0,0 +1,213 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * standard CBC Block Cipher MAC - if no padding is specified the default of + * pad of zeroes is used. + */ + public class CbcBlockCipherMac + : IMac + { + private byte[] mac; + private byte[] Buffer; + private int bufOff; + private IBlockCipher cipher; + private IBlockCipherPadding padding; + private int macSize; + + /** + * create a standard MAC based on a CBC block cipher. This will produce an + * authentication code half the length of the block size of the cipher. + * + * @param cipher the cipher to be used as the basis of the MAC generation. + */ + public CbcBlockCipherMac( + IBlockCipher cipher) + : this(cipher, (cipher.GetBlockSize() * 8) / 2, null) + { + } + + /** + * create a standard MAC based on a CBC block cipher. This will produce an + * authentication code half the length of the block size of the cipher. + * + * @param cipher the cipher to be used as the basis of the MAC generation. + * @param padding the padding to be used to complete the last block. + */ + public CbcBlockCipherMac( + IBlockCipher cipher, + IBlockCipherPadding padding) + : this(cipher, (cipher.GetBlockSize() * 8) / 2, padding) + { + } + + /** + * create a standard MAC based on a block cipher with the size of the + * MAC been given in bits. This class uses CBC mode as the basis for the + * MAC generation. + *

+ * 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. + */ + public CbcBlockCipherMac( + IBlockCipher cipher, + int macSizeInBits) + : this(cipher, macSizeInBits, null) + { + } + + /** + * create a standard MAC based on a block cipher with the size of the + * MAC been given in bits. This class uses CBC mode as the basis for the + * MAC generation. + *

+ * 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. + * @param padding the padding to be used to complete the last block. + */ + public CbcBlockCipherMac( + IBlockCipher cipher, + int macSizeInBits, + IBlockCipherPadding padding) + { + if ((macSizeInBits % 8) != 0) + throw new ArgumentException("MAC size must be multiple of 8"); + + this.cipher = new CbcBlockCipher(cipher); + this.padding = padding; + this.macSize = macSizeInBits / 8; + + mac = new byte[cipher.GetBlockSize()]; + + Buffer = new byte[cipher.GetBlockSize()]; + bufOff = 0; + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + + cipher.Init(true, parameters); + } + + public int GetMacSize() + { + return macSize; + } + + public void Update( + byte input) + { + if (bufOff == Buffer.Length) + { + cipher.ProcessBlock(Buffer, 0, mac, 0); + bufOff = 0; + } + + Buffer[bufOff++] = input; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, Buffer, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(Buffer, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + resultLen += cipher.ProcessBlock(input, inOff, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, Buffer, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] output, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + + if (padding == null) + { + // pad with zeroes + while (bufOff < blockSize) + { + Buffer[bufOff++] = 0; + } + } + else + { + if (bufOff == blockSize) + { + cipher.ProcessBlock(Buffer, 0, mac, 0); + bufOff = 0; + } + + padding.AddPadding(Buffer, bufOff); + } + + cipher.ProcessBlock(Buffer, 0, mac, 0); + + Array.Copy(mac, 0, output, outOff, macSize); + + Reset(); + + return macSize; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + // Clear the buffer. + Array.Clear(Buffer, 0, Buffer.Length); + bufOff = 0; + + // Reset the underlying cipher. + cipher.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/macs/CfbBlockCipherMac.cs b/src/core/srcbc/crypto/macs/CfbBlockCipherMac.cs new file mode 100644 index 0000000..16f870a --- /dev/null +++ b/src/core/srcbc/crypto/macs/CfbBlockCipherMac.cs @@ -0,0 +1,368 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. + */ + class MacCFBBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] cfbV; + private byte[] cfbOutV; + + private readonly int blockSize; + private readonly IBlockCipher cipher; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of the + * feedback mode. + * @param blockSize the block size in bits (note: a multiple of 8) + */ + public MacCFBBlockCipher( + IBlockCipher cipher, + int bitBlockSize) + { + 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()]; + } + + /** + * 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. + * + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length < IV.Length) + { + Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length); + } + else + { + Array.Copy(iv, 0, IV, 0, IV.Length); + } + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(true, parameters); + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/CFB" + * and the block size in bits. + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * return the block size we are operating at. + * + * @return the block size we are operating at (in bytes). + */ + public int GetBlockSize() + { + return blockSize; + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + throw new DataLengthException("input buffer too short"); + + if ((outOff + blockSize) > outBytes.Length) + throw new DataLengthException("output buffer too short"); + + cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); + + // + // XOR the cfbV with the plaintext producing the cipher text + // + for (int i = 0; i < blockSize; i++) + { + outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]); + } + + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize); + + return blockSize; + } + + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + IV.CopyTo(cfbV, 0); + + cipher.Reset(); + } + + public void GetMacBlock( + byte[] mac) + { + cipher.ProcessBlock(cfbV, 0, mac, 0); + } + } + + public class CfbBlockCipherMac + : IMac + { + private byte[] mac; + private byte[] Buffer; + private int bufOff; + private MacCFBBlockCipher cipher; + private IBlockCipherPadding padding; + private int macSize; + + /** + * create a standard MAC based on a CFB block cipher. This will produce an + * authentication code half the length of the block size of the cipher, with + * the CFB mode set to 8 bits. + * + * @param cipher the cipher to be used as the basis of the MAC generation. + */ + public CfbBlockCipherMac( + IBlockCipher cipher) + : this(cipher, 8, (cipher.GetBlockSize() * 8) / 2, null) + { + } + + /** + * create a standard MAC based on a CFB block cipher. This will produce an + * authentication code half the length of the block size of the cipher, with + * the CFB mode set to 8 bits. + * + * @param cipher the cipher to be used as the basis of the MAC generation. + * @param padding the padding to be used. + */ + public CfbBlockCipherMac( + IBlockCipher cipher, + IBlockCipherPadding padding) + : this(cipher, 8, (cipher.GetBlockSize() * 8) / 2, padding) + { + } + + /** + * create a standard MAC based on a block cipher with the size of the + * MAC been given in bits. This class uses CFB mode as the basis for the + * MAC generation. + *

+ * 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 cfbBitSize the size of an output block produced by the CFB mode. + * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8. + */ + public CfbBlockCipherMac( + IBlockCipher cipher, + int cfbBitSize, + int macSizeInBits) + : this(cipher, cfbBitSize, macSizeInBits, null) + { + } + + /** + * create a standard MAC based on a block cipher with the size of the + * MAC been given in bits. This class uses CFB mode as the basis for the + * MAC generation. + *

+ * 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 cfbBitSize the size of an output block produced by the CFB mode. + * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8. + * @param padding a padding to be used. + */ + public CfbBlockCipherMac( + IBlockCipher cipher, + int cfbBitSize, + int macSizeInBits, + IBlockCipherPadding padding) + { + if ((macSizeInBits % 8) != 0) + throw new ArgumentException("MAC size must be multiple of 8"); + + mac = new byte[cipher.GetBlockSize()]; + + this.cipher = new MacCFBBlockCipher(cipher, cfbBitSize); + this.padding = padding; + this.macSize = macSizeInBits / 8; + + Buffer = new byte[this.cipher.GetBlockSize()]; + bufOff = 0; + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + + cipher.Init(true, parameters); + } + + public int GetMacSize() + { + return macSize; + } + + public void Update( + byte input) + { + if (bufOff == Buffer.Length) + { + cipher.ProcessBlock(Buffer, 0, mac, 0); + bufOff = 0; + } + + Buffer[bufOff++] = input; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, Buffer, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(Buffer, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + resultLen += cipher.ProcessBlock(input, inOff, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, Buffer, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] output, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + + // pad with zeroes + if (this.padding == null) + { + while (bufOff < blockSize) + { + Buffer[bufOff++] = 0; + } + } + else + { + padding.AddPadding(Buffer, bufOff); + } + + cipher.ProcessBlock(Buffer, 0, mac, 0); + + cipher.GetMacBlock(mac); + + Array.Copy(mac, 0, output, outOff, macSize); + + Reset(); + + return macSize; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + // Clear the buffer. + Array.Clear(Buffer, 0, Buffer.Length); + bufOff = 0; + + // Reset the underlying cipher. + cipher.Reset(); + } + } + +} diff --git a/src/core/srcbc/crypto/macs/GOST28147Mac.cs b/src/core/srcbc/crypto/macs/GOST28147Mac.cs new file mode 100644 index 0000000..c0116a1 --- /dev/null +++ b/src/core/srcbc/crypto/macs/GOST28147Mac.cs @@ -0,0 +1,296 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * implementation of GOST 28147-89 MAC + */ + public class Gost28147Mac : IMac + { + private const int blockSize = 8; + private const int macSize = 4; + private int bufOff; + private byte[] buf; + private byte[] mac; + private bool firstStep = true; + private int[] workingKey; + + // + // This is default S-box - E_A. + private byte[] S = + { + 0x9,0x6,0x3,0x2,0x8,0xB,0x1,0x7,0xA,0x4,0xE,0xF,0xC,0x0,0xD,0x5, + 0x3,0x7,0xE,0x9,0x8,0xA,0xF,0x0,0x5,0x2,0x6,0xC,0xB,0x4,0xD,0x1, + 0xE,0x4,0x6,0x2,0xB,0x3,0xD,0x8,0xC,0xF,0x5,0xA,0x0,0x7,0x1,0x9, + 0xE,0x7,0xA,0xC,0xD,0x1,0x3,0x9,0x0,0x2,0xB,0x4,0xF,0x8,0x5,0x6, + 0xB,0x5,0x1,0x9,0x8,0xD,0xF,0x0,0xE,0x4,0x2,0x3,0xC,0x7,0xA,0x6, + 0x3,0xA,0xD,0xC,0x1,0x2,0x0,0xB,0x7,0x5,0x9,0x4,0x8,0xF,0xE,0x6, + 0x1,0xD,0x2,0x9,0x7,0xA,0x6,0x0,0x8,0xC,0x4,0x5,0xF,0x3,0xB,0xE, + 0xB,0xA,0xF,0x5,0x0,0xC,0xE,0x8,0x6,0x2,0x3,0x9,0x1,0x7,0xD,0x4 + }; + + public Gost28147Mac() + { + mac = new byte[blockSize]; + buf = new byte[blockSize]; + bufOff = 0; + } + + private static int[] generateWorkingKey( + byte[] userKey) + { + if (userKey.Length != 32) + throw new ArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!"); + + int[] key = new int[8]; + for(int i=0; i!=8; i++) + { + key[i] = bytesToint(userKey,i*4); + } + + return key; + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + buf = new byte[blockSize]; + if (parameters is ParametersWithSBox) + { + ParametersWithSBox param = (ParametersWithSBox)parameters; + + // + // Set the S-Box + // + param.GetSBox().CopyTo(this.S, 0); + + // + // set key if there is one + // + if (param.Parameters != null) + { + workingKey = generateWorkingKey(((KeyParameter)param.Parameters).GetKey()); + } + } + else if (parameters is KeyParameter) + { + workingKey = generateWorkingKey(((KeyParameter)parameters).GetKey()); + } + else + { + throw new ArgumentException("invalid parameter passed to Gost28147 init - " + + parameters.GetType().Name); + } + } + + public string AlgorithmName + { + get { return "Gost28147Mac"; } + } + + public int GetMacSize() + { + return macSize; + } + + private int gost28147_mainStep(int n1, int key) + { + int cm = (key + n1); // CM1 + + // S-box replacing + + int om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4); + om += S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4); + om += S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4); + om += S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4); + om += S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4); + om += S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4); + om += S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4); + om += S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4); + +// return om << 11 | om >>> (32-11); // 11-leftshift + int omLeft = om << 11; + int omRight = (int)(((uint) om) >> (32 - 11)); // Note: Casts required to get unsigned bit rotation + + return omLeft | omRight; + } + + private void gost28147MacFunc( + int[] workingKey, + byte[] input, + int inOff, + byte[] output, + int outOff) + { + int N1, N2, tmp; //tmp -> for saving N1 + N1 = bytesToint(input, inOff); + N2 = bytesToint(input, inOff + 4); + + for (int k = 0; k < 2; k++) // 1-16 steps + { + for (int j = 0; j < 8; j++) + { + tmp = N1; + N1 = N2 ^ gost28147_mainStep(N1, workingKey[j]); // CM2 + N2 = tmp; + } + } + + intTobytes(N1, output, outOff); + intTobytes(N2, output, outOff + 4); + } + + //array of bytes to type int + private static int bytesToint( + byte[] input, + int inOff) + { + return (int)((input[inOff + 3] << 24) & 0xff000000) + ((input[inOff + 2] << 16) & 0xff0000) + + ((input[inOff + 1] << 8) & 0xff00) + (input[inOff] & 0xff); + } + + //int to array of bytes + private static void intTobytes( + int num, + byte[] output, + int outOff) + { + output[outOff + 3] = (byte)(num >> 24); + output[outOff + 2] = (byte)(num >> 16); + output[outOff + 1] = (byte)(num >> 8); + output[outOff] = (byte)num; + } + + private static byte[] CM5func( + byte[] buf, + int bufOff, + byte[] mac) + { + byte[] sum = new byte[buf.Length - bufOff]; + + Array.Copy(buf, bufOff, sum, 0, mac.Length); + + for (int i = 0; i != mac.Length; i++) + { + sum[i] = (byte)(sum[i] ^ mac[i]); + } + + return sum; + } + + public void Update( + byte input) + { + if (bufOff == buf.Length) + { + byte[] sumbuf = new byte[buf.Length]; + Array.Copy(buf, 0, sumbuf, 0, mac.Length); + + if (firstStep) + { + firstStep = false; + } + else + { + sumbuf = CM5func(buf, 0, mac); + } + + gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + bufOff = 0; + } + + buf[bufOff++] = input; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + byte[] sumbuf = new byte[buf.Length]; + Array.Copy(buf, 0, sumbuf, 0, mac.Length); + + if (firstStep) + { + firstStep = false; + } + else + { + sumbuf = CM5func(buf, 0, mac); + } + + gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + sumbuf = CM5func(input, inOff, mac); + gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] output, + int outOff) + { + //padding with zero + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + + byte[] sumbuf = new byte[buf.Length]; + Array.Copy(buf, 0, sumbuf, 0, mac.Length); + + if (firstStep) + { + firstStep = false; + } + else + { + sumbuf = CM5func(buf, 0, mac); + } + + gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + + Array.Copy(mac, (mac.Length/2)-macSize, output, outOff, macSize); + + Reset(); + + return macSize; + } + + public void Reset() + { + // Clear the buffer. + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + firstStep = true; + } + } +} diff --git a/src/core/srcbc/crypto/macs/HMac.cs b/src/core/srcbc/crypto/macs/HMac.cs new file mode 100644 index 0000000..fe12329 --- /dev/null +++ b/src/core/srcbc/crypto/macs/HMac.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * HMAC implementation based on RFC2104 + * + * H(K XOR opad, H(K XOR ipad, text)) + */ + public class HMac : IMac + { + private const byte IPAD = (byte)0x36; + private const byte OPAD = (byte)0x5C; + + private readonly IDigest digest; + private readonly int digestSize; + private readonly int blockLength; + + private byte[] inputPad; + private byte[] outputPad; + + public HMac( + IDigest digest) + { + this.digest = digest; + digestSize = digest.GetDigestSize(); + + blockLength = digest.GetByteLength(); + + inputPad = new byte[blockLength]; + outputPad = new byte[blockLength]; + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "/HMAC"; } + } + + public IDigest GetUnderlyingDigest() + { + return digest; + } + + public void Init( + ICipherParameters parameters) + { + digest.Reset(); + + byte[] key = ((KeyParameter)parameters).GetKey(); + + if (key.Length > blockLength) + { + digest.BlockUpdate(key, 0, key.Length); + digest.DoFinal(inputPad, 0); + for (int i = digestSize; i < inputPad.Length; i++) + { + inputPad[i] = 0; + } + } + else + { + Array.Copy(key, 0, inputPad, 0, key.Length); + for (int i = key.Length; i < inputPad.Length; i++) + { + inputPad[i] = 0; + } + } + + outputPad = new byte[inputPad.Length]; + Array.Copy(inputPad, 0, outputPad, 0, inputPad.Length); + + for (int i = 0; i < inputPad.Length; i++) + { + inputPad[i] ^= IPAD; + } + + for (int i = 0; i < outputPad.Length; i++) + { + outputPad[i] ^= OPAD; + } + + digest.BlockUpdate(inputPad, 0, inputPad.Length); + } + + public int GetMacSize() + { + return digestSize; + } + + public void Update( + byte input) + { + digest.Update(input); + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + digest.BlockUpdate(input, inOff, len); + } + + public int DoFinal( + byte[] output, + int outOff) + { + byte[] tmp = new byte[digestSize]; + digest.DoFinal(tmp, 0); + + digest.BlockUpdate(outputPad, 0, outputPad.Length); + digest.BlockUpdate(tmp, 0, tmp.Length); + + int len = digest.DoFinal(output, outOff); + + Reset(); + + return len; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + /* + * reset the underlying digest. + */ + digest.Reset(); + + /* + * reinitialize the digest. + */ + digest.BlockUpdate(inputPad, 0, inputPad.Length); + } + } +} diff --git a/src/core/srcbc/crypto/macs/ISO9797Alg3Mac.cs b/src/core/srcbc/crypto/macs/ISO9797Alg3Mac.cs new file mode 100644 index 0000000..a8474ed --- /dev/null +++ b/src/core/srcbc/crypto/macs/ISO9797Alg3Mac.cs @@ -0,0 +1,259 @@ +using System; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * DES based CBC Block Cipher MAC according to ISO9797, algorithm 3 (ANSI X9.19 Retail MAC) + * + * This could as well be derived from CBCBlockCipherMac, but then the property mac in the base + * class must be changed to protected + */ + public class ISO9797Alg3Mac : IMac + { + private byte[] mac; + private byte[] buf; + private int bufOff; + private IBlockCipher cipher; + private IBlockCipherPadding padding; + private int macSize; + private KeyParameter lastKey2; + private KeyParameter lastKey3; + + /** + * create a Retail-MAC based on a CBC block cipher. This will produce an + * authentication code of the length of the block size of the cipher. + * + * @param cipher the cipher to be used as the basis of the MAC generation. This must + * be DESEngine. + */ + public ISO9797Alg3Mac( + IBlockCipher cipher) + : this(cipher, cipher.GetBlockSize() * 8, null) + { + } + + /** + * create a Retail-MAC based on a CBC block cipher. This will produce an + * authentication code of the length of the block size of the cipher. + * + * @param cipher the cipher to be used as the basis of the MAC generation. + * @param padding the padding to be used to complete the last block. + */ + public ISO9797Alg3Mac( + IBlockCipher cipher, + IBlockCipherPadding padding) + : this(cipher, cipher.GetBlockSize() * 8, padding) + { + } + + /** + * create a Retail-MAC based on a block cipher with the size of the + * MAC been given in bits. This class uses single DES CBC mode as the basis for the + * MAC generation. + *

+ * 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. + */ + public ISO9797Alg3Mac( + IBlockCipher cipher, + int macSizeInBits) + : this(cipher, macSizeInBits, null) + { + } + + /** + * create a standard MAC based on a block cipher with the size of the + * MAC been given in bits. This class uses single DES CBC mode as the basis for the + * MAC generation. The final block is decrypted and then encrypted using the + * middle and right part of the key. + *

+ * 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. + * @param padding the padding to be used to complete the last block. + */ + public ISO9797Alg3Mac( + IBlockCipher cipher, + int macSizeInBits, + IBlockCipherPadding padding) + { + if ((macSizeInBits % 8) != 0) + throw new ArgumentException("MAC size must be multiple of 8"); + + if (!(cipher is DesEngine)) + throw new ArgumentException("cipher must be instance of DesEngine"); + + this.cipher = new CbcBlockCipher(cipher); + this.padding = padding; + this.macSize = macSizeInBits / 8; + + mac = new byte[cipher.GetBlockSize()]; + buf = new byte[cipher.GetBlockSize()]; + bufOff = 0; + } + + public string AlgorithmName + { + get { return "ISO9797Alg3"; } + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + + if (!(parameters is KeyParameter)) + throw new ArgumentException("parameters must be an instance of KeyParameter"); + + // KeyParameter must contain a double or triple length DES key, + // however the underlying cipher is a single DES. The middle and + // right key are used only in the final step. + + KeyParameter kp = (KeyParameter)parameters; + KeyParameter key1; + byte[] keyvalue = kp.GetKey(); + + if (keyvalue.Length == 16) + { // Double length DES key + key1 = new KeyParameter(keyvalue, 0, 8); + this.lastKey2 = new KeyParameter(keyvalue, 8, 8); + this.lastKey3 = key1; + } + else if (keyvalue.Length == 24) + { // Triple length DES key + key1 = new KeyParameter(keyvalue, 0, 8); + this.lastKey2 = new KeyParameter(keyvalue, 8, 8); + this.lastKey3 = new KeyParameter(keyvalue, 16, 8); + } + else + { + throw new ArgumentException("Key must be either 112 or 168 bit long"); + } + + cipher.Init(true, key1); + } + + public int GetMacSize() + { + return macSize; + } + + public void Update( + byte input) + { + if (bufOff == buf.Length) + { + cipher.ProcessBlock(buf, 0, mac, 0); + bufOff = 0; + } + + buf[bufOff++] = input; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(buf, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + resultLen += cipher.ProcessBlock(input, inOff, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] output, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + + if (padding == null) + { + // pad with zeroes + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + } + else + { + if (bufOff == blockSize) + { + cipher.ProcessBlock(buf, 0, mac, 0); + bufOff = 0; + } + + padding.AddPadding(buf, bufOff); + } + + cipher.ProcessBlock(buf, 0, mac, 0); + + // Added to code from base class + DesEngine deseng = new DesEngine(); + + deseng.Init(false, this.lastKey2); + deseng.ProcessBlock(mac, 0, mac, 0); + + deseng.Init(true, this.lastKey3); + deseng.ProcessBlock(mac, 0, mac, 0); + // **** + + Array.Copy(mac, 0, output, outOff, macSize); + + Reset(); + + return macSize; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + // reset the underlying cipher. + cipher.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/macs/VMPCMac.cs b/src/core/srcbc/crypto/macs/VMPCMac.cs new file mode 100644 index 0000000..46450f4 --- /dev/null +++ b/src/core/srcbc/crypto/macs/VMPCMac.cs @@ -0,0 +1,173 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + public class VmpcMac + : IMac + { + private byte g; + + private byte n = 0; + private byte[] P = null; + private byte s = 0; + + private byte[] T; + private byte[] workingIV; + + private byte[] workingKey; + + private byte x1, x2, x3, x4; + + public virtual int DoFinal(byte[] output, int outOff) + { + // Execute the Post-Processing Phase + for (int r = 1; r < 25; r++) + { + s = P[(s + P[n & 0xff]) & 0xff]; + + x4 = P[(x4 + x3 + r) & 0xff]; + x3 = P[(x3 + x2 + r) & 0xff]; + x2 = P[(x2 + x1 + r) & 0xff]; + x1 = P[(x1 + s + r) & 0xff]; + T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1); + T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2); + T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3); + T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4); + g = (byte) ((g + 4) & 0x1f); + + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte) ((n + 1) & 0xff); + } + + // Input T to the IV-phase of the VMPC KSA + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + T[m & 0x1f]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + // Store 20 new outputs of the VMPC Stream Cipher input table M + byte[] M = new byte[20]; + for (int i = 0; i < 20; i++) + { + s = P[(s + P[i & 0xff]) & 0xff]; + M[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + + byte temp = P[i & 0xff]; + P[i & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + + Array.Copy(M, 0, output, outOff, M.Length); + Reset(); + + return M.Length; + } + + public virtual string AlgorithmName + { + get { return "VMPC-MAC"; } + } + + public virtual int GetMacSize() + { + return 20; + } + + public virtual void Init(ICipherParameters parameters) + { + if (!(parameters is ParametersWithIV)) + throw new ArgumentException("VMPC-MAC Init parameters must include an IV", "parameters"); + + ParametersWithIV ivParams = (ParametersWithIV) parameters; + KeyParameter key = (KeyParameter) ivParams.Parameters; + + if (!(ivParams.Parameters is KeyParameter)) + throw new ArgumentException("VMPC-MAC Init parameters must include a key", "parameters"); + + this.workingIV = ivParams.GetIV(); + + if (workingIV == null || workingIV.Length < 1 || workingIV.Length > 768) + throw new ArgumentException("VMPC-MAC requires 1 to 768 bytes of IV", "parameters"); + + this.workingKey = key.GetKey(); + + Reset(); + + } + + private void initKey(byte[] keyBytes, byte[] ivBytes) + { + s = 0; + P = new byte[256]; + for (int i = 0; i < 256; i++) + { + P[i] = (byte) i; + } + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + for (int m = 0; m < 768; m++) + { + s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff]; + byte temp = P[m & 0xff]; + P[m & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + } + n = 0; + } + + public virtual void Reset() + { + initKey(this.workingKey, this.workingIV); + g = x1 = x2 = x3 = x4 = n = 0; + T = new byte[32]; + for (int i = 0; i < 32; i++) + { + T[i] = 0; + } + } + + public virtual void Update(byte input) + { + s = P[(s + P[n & 0xff]) & 0xff]; + byte c = (byte) (input ^ P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]); + + x4 = P[(x4 + x3) & 0xff]; + x3 = P[(x3 + x2) & 0xff]; + x2 = P[(x2 + x1) & 0xff]; + x1 = P[(x1 + s + c) & 0xff]; + T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1); + T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2); + T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3); + T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4); + g = (byte) ((g + 4) & 0x1f); + + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte) ((n + 1) & 0xff); + } + + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + if ((inOff + len) > input.Length) + throw new DataLengthException("input buffer too short"); + + for (int i = 0; i < len; i++) + { + Update(input[i]); + } + } + } +} diff --git a/src/core/srcbc/crypto/modes/CbcBlockCipher.cs b/src/core/srcbc/crypto/modes/CbcBlockCipher.cs new file mode 100644 index 0000000..34b9103 --- /dev/null +++ b/src/core/srcbc/crypto/modes/CbcBlockCipher.cs @@ -0,0 +1,230 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher. + */ + public class CbcBlockCipher + : IBlockCipher + { + private byte[] IV, cbcV, cbcNextV; + private int blockSize; + private IBlockCipher cipher; + private bool encrypting; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of chaining. + */ + public CbcBlockCipher( + IBlockCipher cipher) + { + this.cipher = cipher; + this.blockSize = cipher.GetBlockSize(); + + this.IV = new byte[blockSize]; + this.cbcV = new byte[blockSize]; + this.cbcNextV = new byte[blockSize]; + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public IBlockCipher 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. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.encrypting = forEncryption; + + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length != blockSize) + { + throw new ArgumentException("initialisation vector must be the same length as block size"); + } + + Array.Copy(iv, 0, IV, 0, iv.Length); + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(encrypting, parameters); + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/CBC". + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/CBC"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + /** + * return the block size of the underlying cipher. + * + * @return the block size of the underlying cipher. + */ + public int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (encrypting) + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + Array.Copy(IV, 0, cbcV, 0, IV.Length); + + cipher.Reset(); + } + + /** + * Do the appropriate chaining step for CBC mode encryption. + * + * @param in the array containing the data to be encrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the encrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + /* + * XOR the cbcV and the input, + * then encrypt the cbcV + */ + for (int i = 0; i < blockSize; i++) + { + cbcV[i] ^= input[inOff + i]; + } + + int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff); + + /* + * copy ciphertext to cbcV + */ + Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length); + + return length; + } + + /** + * Do the appropriate chaining step for CBC mode decryption. + * + * @param in the array containing the data to be decrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the decrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + Array.Copy(input, inOff, cbcNextV, 0, blockSize); + + int length = cipher.ProcessBlock(input, inOff, outBytes, outOff); + + /* + * XOR the cbcV and the output + */ + for (int i = 0; i < blockSize; i++) + { + outBytes[outOff + i] ^= cbcV[i]; + } + + /* + * swap the back up buffer into next position + */ + byte[] tmp; + + tmp = cbcV; + cbcV = cbcNextV; + cbcNextV = tmp; + + return length; + } + } + +} diff --git a/src/core/srcbc/crypto/modes/CcmBlockCipher.cs b/src/core/srcbc/crypto/modes/CcmBlockCipher.cs new file mode 100644 index 0000000..90451b9 --- /dev/null +++ b/src/core/srcbc/crypto/modes/CcmBlockCipher.cs @@ -0,0 +1,345 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * Implements the Counter with Cipher Block Chaining mode (CCM) detailed in + * NIST Special Publication 800-38C. + *

+ * Note: this mode is a packet mode - it needs all the data up front. + *

+ */ + public class CcmBlockCipher + : IAeadBlockCipher + { + private static readonly int BlockSize = 16; + + private readonly IBlockCipher cipher; + private readonly byte[] macBlock; + private bool forEncryption; + private byte[] nonce; + private byte[] associatedText; + private int macSize; + private ICipherParameters keyParam; + private readonly MemoryStream data = new MemoryStream(); + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used. + */ + public CcmBlockCipher( + IBlockCipher cipher) + { + this.cipher = cipher; + this.macBlock = new byte[BlockSize]; + + if (cipher.GetBlockSize() != BlockSize) + throw new ArgumentException("cipher required with a block size of " + BlockSize + "."); + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public virtual IBlockCipher GetUnderlyingCipher() + { + return cipher; + } + + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + if (parameters is AeadParameters) + { + AeadParameters param = (AeadParameters) parameters; + + nonce = param.GetNonce(); + associatedText = param.GetAssociatedText(); + macSize = param.MacSize / 8; + keyParam = param.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) parameters; + + nonce = param.GetIV(); + associatedText = null; + macSize = macBlock.Length / 2; + keyParam = param.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to CCM"); + } + } + + public virtual string AlgorithmName + { + get { return cipher.AlgorithmName + "/CCM"; } + } + + public virtual int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + public virtual int ProcessByte( + byte input, + byte[] outBytes, + int outOff) + { + data.WriteByte(input); + + return 0; + } + + public virtual int ProcessBytes( + byte[] inBytes, + int inOff, + int inLen, + byte[] outBytes, + int outOff) + { + data.Write(inBytes, inOff, inLen); + + return 0; + } + + public virtual int DoFinal( + byte[] outBytes, + int outOff) + { + byte[] text = data.ToArray(); + byte[] enc = ProcessPacket(text, 0, text.Length); + + Array.Copy(enc, 0, outBytes, outOff, enc.Length); + + Reset(); + + return enc.Length; + } + + public virtual void Reset() + { + cipher.Reset(); + data.SetLength(0); + } + + /** + * Returns a byte array containing the mac calculated as part of the + * last encrypt or decrypt operation. + * + * @return the last mac calculated. + */ + public virtual byte[] GetMac() + { + byte[] mac = new byte[macSize]; + + Array.Copy(macBlock, 0, mac, 0, mac.Length); + + return mac; + } + + public virtual int GetUpdateOutputSize( + int len) + { + return 0; + } + + public int GetOutputSize( + int len) + { + if (forEncryption) + { + return (int) data.Length + len + macSize; + } + + return (int) data.Length + len - macSize; + } + + public byte[] ProcessPacket( + byte[] input, + int inOff, + int inLen) + { + if (keyParam == null) + throw new InvalidOperationException("CCM cipher unitialized."); + + IBlockCipher ctrCipher = new SicBlockCipher(cipher); + byte[] iv = new byte[BlockSize]; + byte[] output; + + iv[0] = (byte)(((15 - nonce.Length) - 1) & 0x7); + + Array.Copy(nonce, 0, iv, 1, nonce.Length); + + ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv)); + + if (forEncryption) + { + int index = inOff; + int outOff = 0; + + output = new byte[inLen + macSize]; + + calculateMac(input, inOff, inLen, macBlock); + + ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0); // S0 + + while (index < inLen - BlockSize) // S1... + { + ctrCipher.ProcessBlock(input, index, output, outOff); + outOff += BlockSize; + index += BlockSize; + } + + byte[] block = new byte[BlockSize]; + + Array.Copy(input, index, block, 0, inLen - index); + + ctrCipher.ProcessBlock(block, 0, block, 0); + + Array.Copy(block, 0, output, outOff, inLen - index); + + outOff += inLen - index; + + Array.Copy(macBlock, 0, output, outOff, output.Length - outOff); + } + else + { + int index = inOff; + int outOff = 0; + + output = new byte[inLen - macSize]; + + Array.Copy(input, inOff + inLen - macSize, macBlock, 0, macSize); + + ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0); + + for (int i = macSize; i != macBlock.Length; i++) + { + macBlock[i] = 0; + } + + while (outOff < output.Length - BlockSize) + { + ctrCipher.ProcessBlock(input, index, output, outOff); + outOff += BlockSize; + index += BlockSize; + } + + byte[] block = new byte[BlockSize]; + + Array.Copy(input, index, block, 0, output.Length - outOff); + + ctrCipher.ProcessBlock(block, 0, block, 0); + + Array.Copy(block, 0, output, outOff, output.Length - outOff); + + byte[] calculatedMacBlock = new byte[BlockSize]; + + calculateMac(output, 0, output.Length, calculatedMacBlock); + + if (!Arrays.AreEqual(macBlock, calculatedMacBlock)) + throw new InvalidCipherTextException("mac check in CCM failed"); + } + + return output; + } + + private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock) + { + IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8); + + cMac.Init(keyParam); + + // + // build b0 + // + byte[] b0 = new byte[16]; + + if (hasAssociatedText()) + { + b0[0] |= 0x40; + } + + b0[0] |= (byte)((((cMac.GetMacSize() - 2) / 2) & 0x7) << 3); + + b0[0] |= (byte)(((15 - nonce.Length) - 1) & 0x7); + + Array.Copy(nonce, 0, b0, 1, nonce.Length); + + int q = dataLen; + int count = 1; + while (q > 0) + { + b0[b0.Length - count] = (byte)(q & 0xff); + q >>= 8; + count++; + } + + cMac.BlockUpdate(b0, 0, b0.Length); + + // + // process associated text + // + if (hasAssociatedText()) + { + int extra; + + if (associatedText.Length < ((1 << 16) - (1 << 8))) + { + cMac.Update((byte)(associatedText.Length >> 8)); + cMac.Update((byte)associatedText.Length); + + extra = 2; + } + else // can't go any higher than 2^32 + { + cMac.Update((byte)0xff); + cMac.Update((byte)0xfe); + cMac.Update((byte)(associatedText.Length >> 24)); + cMac.Update((byte)(associatedText.Length >> 16)); + cMac.Update((byte)(associatedText.Length >> 8)); + cMac.Update((byte)associatedText.Length); + + extra = 6; + } + + cMac.BlockUpdate(associatedText, 0, associatedText.Length); + + extra = (extra + associatedText.Length) % 16; + if (extra != 0) + { + for (int i = 0; i != 16 - extra; i++) + { + cMac.Update((byte)0x00); + } + } + } + + // + // add the text + // + cMac.BlockUpdate(data, dataOff, dataLen); + + return cMac.DoFinal(macBlock, 0); + } + + private bool hasAssociatedText() + { + return associatedText != null && associatedText.Length != 0; + } + } +} diff --git a/src/core/srcbc/crypto/modes/CfbBlockCipher.cs b/src/core/srcbc/crypto/modes/CfbBlockCipher.cs new file mode 100644 index 0000000..f83e8fa --- /dev/null +++ b/src/core/srcbc/crypto/modes/CfbBlockCipher.cs @@ -0,0 +1,218 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. + */ + public class CfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] cfbV; + private byte[] cfbOutV; + private bool encrypting; + + private readonly int blockSize; + private readonly IBlockCipher cipher; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of the + * feedback mode. + * @param blockSize the block size in bits (note: a multiple of 8) + */ + public CfbBlockCipher( + IBlockCipher cipher, + int bitBlockSize) + { + 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 IBlockCipher 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. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.encrypting = forEncryption; + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV) parameters; + byte[] iv = ivParam.GetIV(); + int diff = IV.Length - iv.Length; + Array.Copy(iv, 0, IV, diff, iv.Length); + Array.Clear(IV, 0, diff); + + parameters = ivParam.Parameters; + } + Reset(); + cipher.Init(true, parameters); + } + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/CFB" + * and the block size in bits. + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * return the block size we are operating at. + * + * @return the block size we are operating at (in bytes). + */ + public int GetBlockSize() + { + return blockSize; + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (encrypting) + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + /** + * Do the appropriate processing for CFB mode encryption. + * + * @param in the array containing the data to be encrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the encrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + if ((outOff + blockSize) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); + // + // XOR the cfbV with the plaintext producing the cipher text + // + for (int i = 0; i < blockSize; i++) + { + outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]); + } + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize); + return blockSize; + } + /** + * Do the appropriate processing for CFB mode decryption. + * + * @param in the array containing the data to be decrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the encrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + if ((outOff + blockSize) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + Array.Copy(input, inOff, cfbV, cfbV.Length - blockSize, blockSize); + // + // XOR the cfbV with the plaintext producing the plain text + // + for (int i = 0; i < blockSize; i++) + { + outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]); + } + return blockSize; + } + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + Array.Copy(IV, 0, cfbV, 0, IV.Length); + cipher.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/modes/CtsBlockCipher.cs b/src/core/srcbc/crypto/modes/CtsBlockCipher.cs new file mode 100644 index 0000000..eb1ce14 --- /dev/null +++ b/src/core/srcbc/crypto/modes/CtsBlockCipher.cs @@ -0,0 +1,253 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to + * be used to produce cipher text which is the same outLength as the plain text. + */ + public class CtsBlockCipher + : BufferedBlockCipher + { + private readonly int blockSize; + + /** + * Create a buffered block cipher that uses Cipher Text Stealing + * + * @param cipher the underlying block cipher this buffering object wraps. + */ + public CtsBlockCipher( + IBlockCipher cipher) + { + // TODO Should this test for acceptable ones instead? + if (cipher is OfbBlockCipher || cipher is CfbBlockCipher) + throw new ArgumentException("CtsBlockCipher can only accept ECB, or CBC ciphers"); + + this.cipher = cipher; + + blockSize = cipher.GetBlockSize(); + + buf = new byte[blockSize * 2]; + bufOff = 0; + } + + /** + * return the size of the output buffer required for an update of 'length' bytes. + * + * @param length the outLength of the input. + * @return the space required to accommodate a call to update + * with length bytes of input. + */ + public override int GetUpdateOutputSize( + int length) + { + int total = length + 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 length bytes. + * + * @param length the outLength of the input. + * @return the space required to accommodate a call to update and doFinal + * with length bytes of input. + */ + public override int GetOutputSize( + int length) + { + return length + bufOff; + } + + /** + * process a single byte, producing an output block if neccessary. + * + * @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 DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + int resultLen = 0; + + if (bufOff == buf.Length) + { + resultLen = cipher.ProcessBlock(buf, 0, output, outOff); + Debug.Assert(resultLen == blockSize); + + Array.Copy(buf, blockSize, buf, 0, blockSize); + bufOff = blockSize; + } + + buf[bufOff++] = input; + + 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 length 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 DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (length < 0) + { + throw new ArgumentException("Can't have a negative input outLength!"); + } + + int blockSize = GetBlockSize(); + int outLength = GetUpdateOutputSize(length); + + if (outLength > 0) + { + if ((outOff + outLength) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.Length - bufOff; + + if (length > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(buf, 0, output, outOff); + Array.Copy(buf, blockSize, buf, 0, blockSize); + + bufOff = blockSize; + + length -= gapLen; + inOff += gapLen; + + while (length > blockSize) + { + Array.Copy(input, inOff, buf, bufOff, blockSize); + resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen); + Array.Copy(buf, blockSize, buf, 0, blockSize); + + length -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, length); + + bufOff += length; + + 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 DataLengthException if there is insufficient space in out for + * the output. + * @exception InvalidOperationException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if cipher text decrypts wrongly (in + * case the exception will never Get thrown). + */ + public override int DoFinal( + byte[] output, + int outOff) + { + if (bufOff + outOff > output.Length) + { + throw new DataLengthException("output buffer too small in doFinal"); + } + + int blockSize = cipher.GetBlockSize(); + int length = bufOff - blockSize; + byte[] block = new byte[blockSize]; + + if (forEncryption) + { + cipher.ProcessBlock(buf, 0, block, 0); + + if (bufOff < blockSize) + { + throw new DataLengthException("need at least one block of input for CTS"); + } + + for (int i = bufOff; i != buf.Length; i++) + { + buf[i] = block[i - blockSize]; + } + + for (int i = blockSize; i != bufOff; i++) + { + buf[i] ^= block[i - blockSize]; + } + + IBlockCipher c = (cipher is CbcBlockCipher) + ? ((CbcBlockCipher)cipher).GetUnderlyingCipher() + : cipher; + + c.ProcessBlock(buf, blockSize, output, outOff); + + Array.Copy(block, 0, output, outOff + blockSize, length); + } + else + { + byte[] lastBlock = new byte[blockSize]; + + IBlockCipher c = (cipher is CbcBlockCipher) + ? ((CbcBlockCipher)cipher).GetUnderlyingCipher() + : cipher; + + c.ProcessBlock(buf, 0, block, 0); + + for (int i = blockSize; i != bufOff; i++) + { + lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]); + } + + Array.Copy(buf, blockSize, block, 0, length); + + cipher.ProcessBlock(block, 0, output, outOff); + Array.Copy(lastBlock, 0, output, outOff + blockSize, length); + } + + int offset = bufOff; + + Reset(); + + return offset; + } + } +} diff --git a/src/core/srcbc/crypto/modes/EAXBlockCipher.cs b/src/core/srcbc/crypto/modes/EAXBlockCipher.cs new file mode 100644 index 0000000..a069568 --- /dev/null +++ b/src/core/srcbc/crypto/modes/EAXBlockCipher.cs @@ -0,0 +1,302 @@ +using System; + +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * 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 + * 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 + : IAeadBlockCipher + { + private enum Tag : byte { N, H, C }; + + private SicBlockCipher cipher; + + private bool forEncryption; + + private int blockSize; + + private IMac mac; + + private byte[] nonceMac; + private byte[] associatedTextMac; + private byte[] macBlock; + + private int macSize; + private byte[] bufBlock; + private int bufOff; + + /** + * Constructor that accepts an instance of a block cipher engine. + * + * @param cipher the engine to use + */ + public EaxBlockCipher( + IBlockCipher cipher) + { + 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); + } + + public virtual string AlgorithmName + { + get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; } + } + + public virtual int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + byte[] nonce, associatedText; + ICipherParameters keyParam; + + if (parameters is AeadParameters) + { + AeadParameters param = (AeadParameters) parameters; + + nonce = param.GetNonce(); + associatedText = param.GetAssociatedText(); + macSize = param.MacSize / 8; + keyParam = param.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) parameters; + + nonce = param.GetIV(); + associatedText = new byte[0]; + macSize = mac.GetMacSize() / 2; + keyParam = param.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to EAX"); + } + + byte[] tag = new byte[blockSize]; + + mac.Init(keyParam); + tag[blockSize - 1] = (byte) Tag.H; + mac.BlockUpdate(tag, 0, blockSize); + mac.BlockUpdate(associatedText, 0, associatedText.Length); + mac.DoFinal(associatedTextMac, 0); + + tag[blockSize - 1] = (byte) Tag.N; + mac.BlockUpdate(tag, 0, blockSize); + mac.BlockUpdate(nonce, 0, nonce.Length); + mac.DoFinal(nonceMac, 0); + + tag[blockSize - 1] = (byte) Tag.C; + mac.BlockUpdate(tag, 0, blockSize); + + cipher.Init(true, new ParametersWithIV(keyParam, nonceMac)); + } + + private void calculateMac() + { + byte[] outC = new byte[blockSize]; + mac.DoFinal(outC, 0); + + for (int i = 0; i < macBlock.Length; i++) + { + macBlock[i] = (byte)(nonceMac[i] ^ associatedTextMac[i] ^ outC[i]); + } + } + + public virtual void Reset() + { + Reset(true); + } + + private void Reset( + bool clearMac) + { + cipher.Reset(); + mac.Reset(); + + bufOff = 0; + Array.Clear(bufBlock, 0, bufBlock.Length); + + if (clearMac) + { + Array.Clear(macBlock, 0, macBlock.Length); + } + + byte[] tag = new byte[blockSize]; + tag[blockSize - 1] = (byte) Tag.C; + mac.BlockUpdate(tag, 0, blockSize); + } + + public virtual int ProcessByte( + byte input, + byte[] outBytes, + int outOff) + { + return process(input, outBytes, outOff); + } + + public virtual int ProcessBytes( + byte[] inBytes, + int inOff, + int len, + byte[] outBytes, + int outOff) + { + int resultLen = 0; + + for (int i = 0; i != len; i++) + { + resultLen += process(inBytes[inOff + i], outBytes, outOff + resultLen); + } + + return resultLen; + } + + public virtual int DoFinal( + byte[] outBytes, + int outOff) + { + int extra = bufOff; + byte[] tmp = new byte[bufBlock.Length]; + + bufOff = 0; + + if (forEncryption) + { + cipher.ProcessBlock(bufBlock, 0, tmp, 0); + cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize); + + Array.Copy(tmp, 0, outBytes, outOff, extra); + + mac.BlockUpdate(tmp, 0, extra); + + calculateMac(); + + Array.Copy(macBlock, 0, outBytes, outOff + extra, macSize); + + Reset(false); + + return extra + macSize; + } + else + { + if (extra > macSize) + { + mac.BlockUpdate(bufBlock, 0, extra - macSize); + + cipher.ProcessBlock(bufBlock, 0, tmp, 0); + cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize); + + Array.Copy(tmp, 0, outBytes, outOff, extra - macSize); + } + + calculateMac(); + + if (!verifyMac(bufBlock, extra - macSize)) + throw new InvalidCipherTextException("mac check in EAX failed"); + + Reset(false); + + return extra - macSize; + } + } + + public virtual byte[] GetMac() + { + byte[] mac = new byte[macSize]; + + Array.Copy(macBlock, 0, mac, 0, macSize); + + return mac; + } + + public virtual int GetUpdateOutputSize( + int len) + { + return ((len + bufOff) / blockSize) * blockSize; + } + + public virtual int GetOutputSize( + int len) + { + if (forEncryption) + { + return len + bufOff + macSize; + } + + return len + bufOff - macSize; + } + + private int process( + byte b, + byte[] outBytes, + int outOff) + { + bufBlock[bufOff++] = b; + + if (bufOff == bufBlock.Length) + { + int size; + + if (forEncryption) + { + size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff); + + mac.BlockUpdate(outBytes, outOff, blockSize); + } + else + { + mac.BlockUpdate(bufBlock, 0, blockSize); + + size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff); + } + + bufOff = blockSize; + Array.Copy(bufBlock, blockSize, bufBlock, 0, blockSize); + + return size; + } + + return 0; + } + + private bool verifyMac(byte[] mac, int off) + { + for (int i = 0; i < macSize; i++) + { + if (macBlock[i] != mac[off + i]) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/core/srcbc/crypto/modes/GCMBlockCipher.cs b/src/core/srcbc/crypto/modes/GCMBlockCipher.cs new file mode 100644 index 0000000..bdeffe5 --- /dev/null +++ b/src/core/srcbc/crypto/modes/GCMBlockCipher.cs @@ -0,0 +1,447 @@ +using System; + +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /// + /// Implements the Galois/Counter mode (GCM) detailed in + /// NIST Special Publication 800-38D. + /// + public class GcmBlockCipher + : IAeadBlockCipher + { + private const int BlockSize = 16; + private static readonly byte[] Zeroes = new byte[BlockSize]; + private static readonly BigInteger R = new BigInteger("11100001", 2).ShiftLeft(120); + + private readonly IBlockCipher cipher; + + // These fields are set by Init and not modified by processing + private bool forEncryption; + private int macSize; + private byte[] nonce; + private byte[] A; + private KeyParameter keyParam; + // private int tagLength; + private BigInteger H; + private BigInteger initS; + private byte[] J0; + + // These fields are modified during processing + private byte[] bufBlock; + private byte[] macBlock; + private BigInteger S; + private byte[] counter; + private int bufOff; + private long totalLength; + + // Debug variables + // private int nCount, xCount, yCount; + + public GcmBlockCipher( + IBlockCipher c) + { + if (c.GetBlockSize() != BlockSize) + throw new ArgumentException("cipher required with a block size of " + BlockSize + "."); + + this.cipher = c; + } + + public virtual string AlgorithmName + { + get { return cipher.AlgorithmName + "/GCM"; } + } + + public virtual int GetBlockSize() + { + return BlockSize; + } + + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + this.macSize = 16; // TODO Make configurable? + this.macBlock = null; + + // TODO If macSize limitation is removed, be very careful about bufBlock + int bufLength = forEncryption ? BlockSize : (BlockSize + macSize); + this.bufBlock = new byte[bufLength]; + + if (parameters is AeadParameters) + { + AeadParameters param = (AeadParameters)parameters; + + nonce = param.GetNonce(); + A = param.GetAssociatedText(); + // macSize = param.getMacSize() / 8; + if (param.MacSize != 128) + { + // TODO Make configurable? + throw new ArgumentException("only 128-bit MAC supported currently"); + } + keyParam = param.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV)parameters; + + nonce = param.GetIV(); + A = null; + keyParam = (KeyParameter)param.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to GCM"); + } + + if (nonce == null || nonce.Length < 1) + { + throw new ArgumentException("IV must be at least 1 byte"); + } + + if (A == null) + { + // Avoid lots of null checks + A = new byte[0]; + } + + // Cipher always used input forward mode + cipher.Init(true, keyParam); + + // TODO This should be configurable by Init parameters + // (but must be 16 if nonce length not 12) (BlockSize?) + // this.tagLength = 16; + + byte[] h = new byte[BlockSize]; + cipher.ProcessBlock(Zeroes, 0, h, 0); + //trace("H: " + new string(Hex.encode(h))); + this.H = new BigInteger(1, h); + this.initS = gHASH(A, false); + + if (nonce.Length == 12) + { + this.J0 = new byte[16]; + Array.Copy(nonce, 0, J0, 0, nonce.Length); + this.J0[15] = 0x01; + } + else + { + BigInteger N = gHASH(nonce, true); + BigInteger X = BigInteger.ValueOf(nonce.Length * 8); + //trace("len({})||len(IV): " + dumpBigInt(X)); + + N = multiply(N.Xor(X), H); + //trace("GHASH(H,{},IV): " + dumpBigInt(N)); + this.J0 = asBlock(N); + } + + this.S = initS; + this.counter = Arrays.Clone(J0); + //trace("Y" + yCount + ": " + new string(Hex.encode(counter))); + this.bufOff = 0; + this.totalLength = 0; + } + + public virtual byte[] GetMac() + { + return Arrays.Clone(macBlock); + } + + public virtual int GetOutputSize( + int len) + { + if (forEncryption) + { + return len + bufOff + macSize; + } + + return len + bufOff - macSize; + } + + public virtual int GetUpdateOutputSize( + int len) + { + return ((len + bufOff) / BlockSize) * BlockSize; + } + + public virtual int ProcessByte( + byte input, + byte[] output, + int outOff) + { + return Process(input, output, outOff); + } + + public virtual int ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + int resultLen = 0; + + for (int i = 0; i != len; i++) + { + resultLen += Process(input[inOff + i], output, outOff + resultLen); + } + + return resultLen; + } + + private int Process( + byte input, + byte[] output, + int outOff) + { + bufBlock[bufOff++] = input; + + if (bufOff == bufBlock.Length) + { + gCTRBlock(bufBlock, BlockSize, output, outOff); + if (!forEncryption) + { + Array.Copy(bufBlock, BlockSize, bufBlock, 0, BlockSize); + } + // bufOff = 0; + bufOff = bufBlock.Length - BlockSize; + // return bufBlock.Length; + return BlockSize; + } + + return 0; + } + + public int DoFinal(byte[] output, int outOff) + { + int extra = bufOff; + if (!forEncryption) + { + if (extra < macSize) + throw new InvalidCipherTextException("data too short"); + + extra -= macSize; + } + + if (extra > 0) + { + byte[] tmp = new byte[BlockSize]; + Array.Copy(bufBlock, 0, tmp, 0, extra); + gCTRBlock(tmp, extra, output, outOff); + } + + // Final gHASH + BigInteger X = BigInteger.ValueOf(A.Length * 8).ShiftLeft(64).Add( + BigInteger.ValueOf(totalLength * 8)); + //trace("len(A)||len(C): " + dumpBigInt(X)); + + S = multiply(S.Xor(X), H); + //trace("GHASH(H,A,C): " + dumpBigInt(S)); + + // T = MSBt(GCTRk(J0,S)) + byte[] tBytes = new byte[BlockSize]; + cipher.ProcessBlock(J0, 0, tBytes, 0); + //trace("E(K,Y0): " + new string(Hex.encode(tmp))); + BigInteger T = S.Xor(new BigInteger(1, tBytes)); + + // TODO Fix this if tagLength becomes configurable + byte[] tag = asBlock(T); + //trace("T: " + new string(Hex.encode(tag))); + + int resultLen = extra; + + if (forEncryption) + { + this.macBlock = tag; + Array.Copy(tag, 0, output, outOff + bufOff, tag.Length); + resultLen += tag.Length; + } + else + { + this.macBlock = new byte[macSize]; + Array.Copy(bufBlock, extra, macBlock, 0, macSize); + if (!Arrays.AreEqual(tag, this.macBlock)) + throw new InvalidCipherTextException("mac check input GCM failed"); + } + + Reset(false); + + return resultLen; + } + + public virtual void Reset() + { + Reset(true); + } + + private void Reset( + bool clearMac) + { + // Debug + // nCount = xCount = yCount = 0; + + S = initS; + counter = Arrays.Clone(J0); + bufOff = 0; + totalLength = 0; + + if (bufBlock != null) + { + Array.Clear(bufBlock, 0, bufBlock.Length); + } + + if (clearMac) + { + macBlock = null; + } + + cipher.Reset(); + } + + private void gCTRBlock(byte[] buf, int bufCount, byte[] output, int outOff) + { + inc(counter); + //trace("Y" + ++yCount + ": " + new string(Hex.encode(counter))); + + byte[] tmp = new byte[BlockSize]; + cipher.ProcessBlock(counter, 0, tmp, 0); + //trace("E(K,Y" + yCount + "): " + new string(Hex.encode(tmp))); + + if (forEncryption) + { + Array.Copy(Zeroes, bufCount, tmp, bufCount, BlockSize - bufCount); + + for (int i = bufCount - 1; i >= 0; --i) + { + tmp[i] ^= buf[i]; + output[outOff + i] = tmp[i]; + } + + gHASHBlock(tmp); + } + else + { + for (int i = bufCount - 1; i >= 0; --i) + { + tmp[i] ^= buf[i]; + output[outOff + i] = tmp[i]; + } + + gHASHBlock(buf); + } + + totalLength += bufCount; + } + + private BigInteger gHASH(byte[] b, bool nonce) + { + //trace("" + b.Length); + BigInteger Y = BigInteger.Zero; + + for (int pos = 0; pos < b.Length; pos += 16) + { + byte[] x = new byte[16]; + int num = System.Math.Min(b.Length - pos, 16); + Array.Copy(b, pos, x, 0, num); + BigInteger X = new BigInteger(1, x); + Y = multiply(Y.Xor(X), H); + // if (nonce) + // { + // trace("N" + ++nCount + ": " + dumpBigInt(Y)); + // } + // else + // { + // trace("X" + ++xCount + ": " + dumpBigInt(Y) + " (gHASH)"); + // } + } + + return Y; + } + + private void gHASHBlock(byte[] block) + { + if (block.Length > BlockSize) + { + byte[] tmp = new byte[BlockSize]; + Array.Copy(block, 0, tmp, 0, BlockSize); + block = tmp; + } + + BigInteger X = new BigInteger(1, block); + S = multiply(S.Xor(X), H); + //trace("X" + ++xCount + ": " + dumpBigInt(S) + " (gHASHBlock)"); + } + + private static void inc(byte[] block) + { + // assert block.Length == 16; + + for (int i = 15; i >= 12; --i) + { + byte b = (byte)((block[i] + 1) & 0xff); + block[i] = b; + + if (b != 0) + { + break; + } + } + } + + private BigInteger multiply( + BigInteger X, + BigInteger Y) + { + BigInteger Z = BigInteger.Zero; + BigInteger V = X; + + for (int i = 0; i < 128; ++i) + { + if (Y.TestBit(127 - i)) + { + Z = Z.Xor(V); + } + + bool lsb = V.TestBit(0); + V = V.ShiftRight(1); + if (lsb) + { + V = V.Xor(R); + } + } + + return Z; + } + + private byte[] asBlock( + BigInteger bi) + { + byte[] b = BigIntegers.AsUnsignedByteArray(bi); + if (b.Length < 16) + { + byte[] tmp = new byte[16]; + Array.Copy(b, 0, tmp, tmp.Length - b.Length, b.Length); + b = tmp; + } + return b; + } + + // private string dumpBigInt(BigInteger bi) + // { + // byte[] b = asBlock(bi); + // + // return new string(Hex.encode(b)); + // } + // + // private void trace(string msg) + // { + // System.err.println(msg); + // } + } +} \ No newline at end of file diff --git a/src/core/srcbc/crypto/modes/GOFBBlockCipher.cs b/src/core/srcbc/crypto/modes/GOFBBlockCipher.cs new file mode 100644 index 0000000..9ddde26 --- /dev/null +++ b/src/core/srcbc/crypto/modes/GOFBBlockCipher.cs @@ -0,0 +1,223 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * implements the GOST 28147 OFB counter mode (GCTR). + */ + public class GOfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] ofbV; + private byte[] ofbOutV; + + private readonly int blockSize; + private readonly IBlockCipher cipher; + + bool firstStep = true; + int N3; + int N4; + const int C1 = 16843012; //00000001000000010000000100000100 + const int C2 = 16843009; //00000001000000010000000100000001 + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of the + * counter mode (must have a 64 bit block size). + */ + public GOfbBlockCipher( + IBlockCipher cipher) + { + this.cipher = cipher; + this.blockSize = cipher.GetBlockSize(); + + if (blockSize != 8) + { + throw new ArgumentException("GCTR only for 64 bit block ciphers"); + } + + this.IV = new byte[cipher.GetBlockSize()]; + this.ofbV = new byte[cipher.GetBlockSize()]; + this.ofbOutV = new byte[cipher.GetBlockSize()]; + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public IBlockCipher 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. + * + * @param encrypting if true the cipher is initialised for + * encryption, if false for decryption. + * @param parameters the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is inappropriate. + */ + public void Init( + bool forEncryption, //ignored by this CTR mode + ICipherParameters parameters) + { + firstStep = true; + N3 = 0; + N4 = 0; + + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length < IV.Length) + { + // prepend the supplied IV with zeros (per FIPS PUB 81) + Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length); + for (int i = 0; i < IV.Length - iv.Length; i++) + { + IV[i] = 0; + } + } + else + { + Array.Copy(iv, 0, IV, 0, IV.Length); + } + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(true, parameters); + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/GCTR" + * and the block size in bits + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/GCTR"; } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * return the block size we are operating at (in bytes). + * + * @return the block size we are operating at (in bytes). + */ + public int GetBlockSize() + { + return blockSize; + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > output.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++) + { + output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]); + } + + // + // change over the input block. + // + Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize); + Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize); + + return blockSize; + } + + /** + * reset the feedback vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + Array.Copy(IV, 0, ofbV, 0, IV.Length); + + cipher.Reset(); + } + + //array of bytes to type int + private int bytesToint( + byte[] inBytes, + int inOff) + { + return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) + + ((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff); + } + + //int to array of bytes + private void intTobytes( + int num, + byte[] outBytes, + int outOff) + { + outBytes[outOff + 3] = (byte)(num >> 24); + outBytes[outOff + 2] = (byte)(num >> 16); + outBytes[outOff + 1] = (byte)(num >> 8); + outBytes[outOff] = (byte)num; + } + } +} diff --git a/src/core/srcbc/crypto/modes/IAeadBlockCipher.cs b/src/core/srcbc/crypto/modes/IAeadBlockCipher.cs new file mode 100644 index 0000000..1a7eb98 --- /dev/null +++ b/src/core/srcbc/crypto/modes/IAeadBlockCipher.cs @@ -0,0 +1,90 @@ +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /// + /// A block cipher mode that includes authenticated encryption with a streaming mode + /// and optional associated data. + /// + public interface IAeadBlockCipher + { + /// The name of the algorithm this cipher implements. + string AlgorithmName { get; } + + /// Initialise the cipher. + /// Parameter can either be an AeadParameters or a ParametersWithIV object. + /// Initialise for encryption if true, for decryption if false. + /// The key or other data required by the cipher. + void Init(bool forEncryption, ICipherParameters parameters); + + /// The block size for this cipher, in bytes. + int GetBlockSize(); + + /** + * Encrypt/decrypt a single byte. + * + * @param input the byte to be processed. + * @param outBytes the output buffer the processed byte goes into. + * @param outOff the offset into the output byte array the processed data starts at. + * @return the number of bytes written to out. + * @exception DataLengthException if the output buffer is too small. + */ + int ProcessByte(byte input, byte[] outBytes, int outOff); + + /** + * Process a block of bytes from in putting the result into out. + * + * @param inBytes 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 outBytes 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 written to out. + * @exception DataLengthException if the output buffer is too small. + */ + int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff); + + /** + * Finish the operation either appending or verifying the MAC at the end of the data. + * + * @param outBytes space for any resulting output data. + * @param outOff offset into out to start copying the data at. + * @return number of bytes written into out. + * @throws InvalidOperationException if the cipher is in an inappropriate state. + * @throws InvalidCipherTextException if the MAC fails to match. + */ + int DoFinal(byte[] outBytes, int outOff); + + /** + * Return the value of the MAC associated with the last stream processed. + * + * @return MAC for plaintext data. + */ + byte[] GetMac(); + + /** + * Return the size of the output buffer required for a ProcessBytes + * an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to ProcessBytes + * with len bytes of input. + */ + int GetUpdateOutputSize(int len); + + /** + * Return the size of the output buffer required for a ProcessBytes 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 ProcessBytes and DoFinal + * with len bytes of input. + */ + int GetOutputSize(int len); + + /// + /// Reset the cipher to the same state as it was after the last init (if there was one). + /// + void Reset(); + } +} diff --git a/src/core/srcbc/crypto/modes/OfbBlockCipher.cs b/src/core/srcbc/crypto/modes/OfbBlockCipher.cs new file mode 100644 index 0000000..4929f80 --- /dev/null +++ b/src/core/srcbc/crypto/modes/OfbBlockCipher.cs @@ -0,0 +1,178 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * implements a Output-FeedBack (OFB) mode on top of a simple cipher. + */ + public class OfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] ofbV; + private byte[] ofbOutV; + + private readonly int blockSize; + private readonly IBlockCipher cipher; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of the + * feedback mode. + * @param blockSize the block size in bits (note: a multiple of 8) + */ + public OfbBlockCipher( + IBlockCipher cipher, + int blockSize) + { + this.cipher = cipher; + this.blockSize = blockSize / 8; + + this.IV = new byte[cipher.GetBlockSize()]; + this.ofbV = new byte[cipher.GetBlockSize()]; + this.ofbOutV = new byte[cipher.GetBlockSize()]; + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public IBlockCipher 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. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, //ignored by this OFB mode + ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length < IV.Length) + { + // prepend the supplied IV with zeros (per FIPS PUB 81) + Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length); + for (int i = 0; i < IV.Length - iv.Length; i++) + { + IV[i] = 0; + } + } + else + { + Array.Copy(iv, 0, IV, 0, IV.Length); + } + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(true, parameters); + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/OFB" + * and the block size in bits + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/OFB" + (blockSize * 8); } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * return the block size we are operating at (in bytes). + * + * @return the block size we are operating at (in bytes). + */ + public int GetBlockSize() + { + return blockSize; + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > output.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++) + { + output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]); + } + + // + // change over the input block. + // + Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize); + Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize); + + return blockSize; + } + + /** + * reset the feedback vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + Array.Copy(IV, 0, ofbV, 0, IV.Length); + + cipher.Reset(); + } + } + +} diff --git a/src/core/srcbc/crypto/modes/OpenPgpCfbBlockCipher.cs b/src/core/srcbc/crypto/modes/OpenPgpCfbBlockCipher.cs new file mode 100644 index 0000000..c5265d1 --- /dev/null +++ b/src/core/srcbc/crypto/modes/OpenPgpCfbBlockCipher.cs @@ -0,0 +1,344 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode + * on top of a simple cipher. This class assumes the IV has been prepended + * to the data stream already, and just accomodates the reset after + * (blockSize + 2) bytes have been read. + *

+ * For further info see RFC 2440. + *

+ */ + public class OpenPgpCfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] FR; + private byte[] FRE; + private byte[] tmp; + + private readonly IBlockCipher cipher; + private readonly int blockSize; + + private int count; + private bool forEncryption; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of the + * feedback mode. + */ + public OpenPgpCfbBlockCipher( + IBlockCipher cipher) + { + this.cipher = cipher; + + this.blockSize = cipher.GetBlockSize(); + this.IV = new byte[blockSize]; + this.FR = new byte[blockSize]; + this.FRE = new byte[blockSize]; + this.tmp = new byte[blockSize]; + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public IBlockCipher GetUnderlyingCipher() + { + return cipher; + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/PGPCFB" + * and the block size in bits. + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/OpenPGPCFB"; } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * return the block size we are operating at. + * + * @return the block size we are operating at (in bytes). + */ + public int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff); + } + + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + count = 0; + + Array.Copy(IV, 0, FR, 0, FR.Length); + + cipher.Reset(); + } + + /** + * 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. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param parameters the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length < IV.Length) + { + // prepend the supplied IV with zeros (per FIPS PUB 81) + Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length); + for (int i = 0; i < IV.Length - iv.Length; i++) + { + IV[i] = 0; + } + } + else + { + Array.Copy(iv, 0, IV, 0, IV.Length); + } + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(true, parameters); + } + + /** + * Encrypt one byte of data according to CFB mode. + * @param data the byte to encrypt + * @param blockOff offset in the current block + * @returns the encrypted byte + */ + private byte EncryptByte(byte data, int blockOff) + { + return (byte)(FRE[blockOff] ^ data); + } + + /** + * Do the appropriate processing for CFB IV mode encryption. + * + * @param in the array containing the data to be encrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the encrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (count > blockSize) + { + FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2); + FR[blockSize - 1] = outBytes[outOff + 1] = EncryptByte(input[inOff + 1], blockSize - 1); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2); + } + + Array.Copy(outBytes, outOff + 2, FR, 0, blockSize - 2); + } + else if (count == 0) + { + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 0; n < blockSize; n++) + { + outBytes[outOff + n] = EncryptByte(input[inOff + n], n); + } + + Array.Copy(outBytes, outOff, FR, 0, blockSize); + + count += blockSize; + } + else if (count == blockSize) + { + cipher.ProcessBlock(FR, 0, FRE, 0); + + outBytes[outOff] = EncryptByte(input[inOff], 0); + outBytes[outOff + 1] = EncryptByte(input[inOff + 1], 1); + + // + // do reset + // + Array.Copy(FR, 2, FR, 0, blockSize - 2); + Array.Copy(outBytes, outOff, FR, blockSize - 2, 2); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2); + } + + Array.Copy(outBytes, outOff + 2, FR, 0, blockSize - 2); + + count += blockSize; + } + + return blockSize; + } + + /** + * Do the appropriate processing for CFB IV mode decryption. + * + * @param in the array containing the data to be decrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the encrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (count > blockSize) + { + // copy in buffer so that this mode works if in and out are the same + Array.Copy(input, inOff, tmp, 0, blockSize); + + outBytes[outOff] = EncryptByte(tmp[0], blockSize - 2); + outBytes[outOff + 1] = EncryptByte(tmp[1], blockSize - 1); + + Array.Copy(tmp, 0, FR, blockSize - 2, 2); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + outBytes[outOff + n] = EncryptByte(tmp[n], n - 2); + } + + Array.Copy(tmp, 2, FR, 0, blockSize - 2); + } + else if (count == 0) + { + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 0; n < blockSize; n++) + { + FR[n] = input[inOff + n]; + outBytes[n] = EncryptByte(input[inOff + n], n); + } + + count += blockSize; + } + else if (count == blockSize) + { + Array.Copy(input, inOff, tmp, 0, blockSize); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + outBytes[outOff] = EncryptByte(tmp[0], 0); + outBytes[outOff + 1] = EncryptByte(tmp[1], 1); + + Array.Copy(FR, 2, FR, 0, blockSize - 2); + + FR[blockSize - 2] = tmp[0]; + FR[blockSize - 1] = tmp[1]; + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + FR[n - 2] = input[inOff + n]; + outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2); + } + + count += blockSize; + } + + return blockSize; + } + } +} diff --git a/src/core/srcbc/crypto/modes/SicBlockCipher.cs b/src/core/srcbc/crypto/modes/SicBlockCipher.cs new file mode 100644 index 0000000..9ccf76b --- /dev/null +++ b/src/core/srcbc/crypto/modes/SicBlockCipher.cs @@ -0,0 +1,106 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * Implements the Segmented Integer Counter (SIC) mode on top of a simple + * block cipher. + */ + public class SicBlockCipher + : IBlockCipher + { + private readonly IBlockCipher cipher; + private readonly int blockSize; + private readonly byte[] IV; + private readonly byte[] counter; + private readonly byte[] counterOut; + + /** + * Basic constructor. + * + * @param c the block cipher to be used. + */ + public SicBlockCipher(IBlockCipher cipher) + { + this.cipher = cipher; + this.blockSize = cipher.GetBlockSize(); + this.IV = new byte[blockSize]; + this.counter = new byte[blockSize]; + this.counterOut = new byte[blockSize]; + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public IBlockCipher GetUnderlyingCipher() + { + return cipher; + } + + public void Init( + bool forEncryption, //ignored by this CTR mode + ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV) parameters; + byte[] iv = ivParam.GetIV(); + Array.Copy(iv, 0, IV, 0, IV.Length); + + Reset(); + cipher.Init(true, ivParam.Parameters); + } + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/SIC"; } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + public int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + cipher.ProcessBlock(counter, 0, counterOut, 0); + + // + // XOR the counterOut with the plaintext producing the cipher text + // + for (int i = 0; i < counterOut.Length; i++) + { + output[outOff + i] = (byte)(counterOut[i] ^ input[inOff + i]); + } + + // Increment the counter + int j = counter.Length; + while (--j >= 0 && ++counter[j] == 0) + { + } + + return counter.Length; + } + + public void Reset() + { + Array.Copy(IV, 0, counter, 0, counter.Length); + cipher.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/paddings/BlockCipherPadding.cs b/src/core/srcbc/crypto/paddings/BlockCipherPadding.cs new file mode 100644 index 0000000..2ab64fa --- /dev/null +++ b/src/core/srcbc/crypto/paddings/BlockCipherPadding.cs @@ -0,0 +1,43 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * Block cipher padders are expected to conform to this interface + */ + public interface IBlockCipherPadding + { + /** + * Initialise the padder. + * + * @param param parameters, if any required. + */ + void Init(SecureRandom random); + //throws ArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + string PaddingName { get; } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + int AddPadding(byte[] input, int inOff); + + /** + * return the number of pad bytes present in the block. + * @exception InvalidCipherTextException if the padding is badly formed + * or invalid. + */ + int PadCount(byte[] input); + //throws InvalidCipherTextException; + } + +} diff --git a/src/core/srcbc/crypto/paddings/ISO10126d2Padding.cs b/src/core/srcbc/crypto/paddings/ISO10126d2Padding.cs new file mode 100644 index 0000000..890a502 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/ISO10126d2Padding.cs @@ -0,0 +1,76 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + + +namespace Org.BouncyCastle.Crypto.Paddings +{ + + /** + * A padder that adds ISO10126-2 padding to a block. + */ + public class ISO10126d2Padding: IBlockCipherPadding + { + private SecureRandom random; + + /** + * Initialise the padder. + * + * @param random a SecureRandom if available. + */ + public void Init( + SecureRandom random) + //throws ArgumentException + { + this.random = (random != null) ? random : new SecureRandom(); + } + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public string PaddingName + { + get { return "ISO10126-2"; } + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int AddPadding( + byte[] input, + int inOff) + { + byte code = (byte)(input.Length - inOff); + + while (inOff < (input.Length - 1)) + { + input[inOff] = (byte)random.NextInt(); + inOff++; + } + + input[inOff] = code; + + return code; + } + + /** + * return the number of pad bytes present in the block. + */ + public int PadCount(byte[] input) + //throws InvalidCipherTextException + { + int count = input[input.Length - 1] & 0xff; + + if (count > input.Length) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + return count; + } + } + +} diff --git a/src/core/srcbc/crypto/paddings/ISO7816d4Padding.cs b/src/core/srcbc/crypto/paddings/ISO7816d4Padding.cs new file mode 100644 index 0000000..1c8c5f0 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/ISO7816d4Padding.cs @@ -0,0 +1,79 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * A padder that adds the padding according to the scheme referenced in + * ISO 7814-4 - scheme 2 from ISO 9797-1. The first byte is 0x80, rest is 0x00 + */ + public class ISO7816d4Padding + : IBlockCipherPadding + { + /** + * Initialise the padder. + * + * @param random - a SecureRandom if available. + */ + public void Init( + SecureRandom random) + { + // nothing to do. + } + + /** + * Return the name of the algorithm the padder implements. + * + * @return the name of the algorithm the padder implements. + */ + public string PaddingName + { + get { return "ISO7816-4"; } + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int AddPadding( + byte[] input, + int inOff) + { + int added = (input.Length - inOff); + + input[inOff]= (byte) 0x80; + inOff ++; + + while (inOff < input.Length) + { + input[inOff] = (byte) 0; + inOff++; + } + + return added; + } + + /** + * return the number of pad bytes present in the block. + */ + public int PadCount( + byte[] input) + { + int count = input.Length - 1; + + while (count > 0 && input[count] == 0) + { + count--; + } + + if (input[count] != (byte)0x80) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + return input.Length - count; + } + } +} diff --git a/src/core/srcbc/crypto/paddings/PaddedBufferedBlockCipher.cs b/src/core/srcbc/crypto/paddings/PaddedBufferedBlockCipher.cs new file mode 100644 index 0000000..6b32fa0 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/PaddedBufferedBlockCipher.cs @@ -0,0 +1,287 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * A wrapper class that allows block ciphers to be used to process data in + * a piecemeal fashion with padding. The PaddedBufferedBlockCipher + * outputs a block only when the buffer is full and more data is being added, + * or on a doFinal (unless the current block in the buffer is a pad block). + * The default padding mechanism used is the one outlined in Pkcs5/Pkcs7. + */ + public class PaddedBufferedBlockCipher + : BufferedBlockCipher + { + private readonly IBlockCipherPadding padding; + + /** + * Create a buffered block cipher with the desired padding. + * + * @param cipher the underlying block cipher this buffering object wraps. + * @param padding the padding type. + */ + public PaddedBufferedBlockCipher( + IBlockCipher cipher, + IBlockCipherPadding padding) + { + this.cipher = cipher; + this.padding = padding; + + buf = new byte[cipher.GetBlockSize()]; + bufOff = 0; + } + + /** + * Create a buffered block cipher Pkcs7 padding + * + * @param cipher the underlying block cipher this buffering object wraps. + */ + public PaddedBufferedBlockCipher(IBlockCipher cipher) + : this(cipher, new Pkcs7Padding()) { } + + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + SecureRandom initRandom = null; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom)parameters; + initRandom = p.Random; + parameters = p.Parameters; + } + + Reset(); + padding.Init(initRandom); + cipher.Init(forEncryption, parameters); + } + + /** + * return the minimum 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 override int GetOutputSize( + int length) + { + int total = length + bufOff; + int leftOver = total % buf.Length; + + if (leftOver == 0) + { + if (forEncryption) + { + return total + buf.Length; + } + + return total; + } + + return total - leftOver + buf.Length; + } + + /** + * 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 override int GetUpdateOutputSize( + int length) + { + int total = length + bufOff; + int leftOver = total % buf.Length; + + if (leftOver == 0) + { + return total - buf.Length; + } + + return total - leftOver; + } + + /** + * process a single byte, producing an output block if neccessary. + * + * @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 DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + int resultLen = 0; + + if (bufOff == buf.Length) + { + resultLen = cipher.ProcessBlock(buf, 0, output, outOff); + bufOff = 0; + } + + buf[bufOff++] = input; + + 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 DataLengthException if there isn't enough space in out. + * @exception InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (length < 0) + { + throw new ArgumentException("Can't have a negative input length!"); + } + + int blockSize = GetBlockSize(); + int outLength = GetUpdateOutputSize(length); + + if (outLength > 0) + { + if ((outOff + outLength) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.Length - bufOff; + + if (length > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(buf, 0, output, outOff); + + bufOff = 0; + length -= gapLen; + inOff += gapLen; + + while (length > buf.Length) + { + resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen); + + length -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, length); + + bufOff += length; + + return resultLen; + } + + /** + * Process the last block in the buffer. If the buffer is currently + * full and padding needs to be added a call to doFinal will produce + * 2 * GetBlockSize() bytes. + * + * @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 DataLengthException if there is insufficient space in out for + * the output or we are decrypting and the input is not block size aligned. + * @exception InvalidOperationException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if padding is expected and not found. + */ + public override int DoFinal( + byte[] output, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + + if (forEncryption) + { + if (bufOff == blockSize) + { + if ((outOff + 2 * blockSize) > output.Length) + { + Reset(); + + throw new DataLengthException("output buffer too short"); + } + + resultLen = cipher.ProcessBlock(buf, 0, output, outOff); + bufOff = 0; + } + + padding.AddPadding(buf, bufOff); + + resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen); + + Reset(); + } + else + { + if (bufOff == blockSize) + { + resultLen = cipher.ProcessBlock(buf, 0, buf, 0); + bufOff = 0; + } + else + { + Reset(); + + throw new DataLengthException("last block incomplete in decryption"); + } + + try + { + resultLen -= padding.PadCount(buf); + + Array.Copy(buf, 0, output, outOff, resultLen); + } + finally + { + Reset(); + } + } + + return resultLen; + } + } + +} diff --git a/src/core/srcbc/crypto/paddings/Pkcs7Padding.cs b/src/core/srcbc/crypto/paddings/Pkcs7Padding.cs new file mode 100644 index 0000000..c72a108 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/Pkcs7Padding.cs @@ -0,0 +1,77 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * A padder that adds Pkcs7/Pkcs5 padding to a block. + */ + public class Pkcs7Padding: IBlockCipherPadding + { + /** + * Initialise the padder. + * + * @param random - a SecureRandom if available. + */ + public void Init(SecureRandom random) + { + // nothing to do. + } + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public string PaddingName + { + get { return "PKCS7"; } + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int AddPadding( + byte[] input, + int inOff) + { + byte code = (byte)(input.Length - inOff); + + while (inOff < input.Length) + { + input[inOff] = code; + inOff++; + } + + return code; + } + + /** + * return the number of pad bytes present in the block. + */ + public int PadCount( + byte[] input) + { + int count = (int) input[input.Length - 1]; + + if (count < 1 || count > input.Length) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + for (int i = 1; i <= count; i++) + { + if (input[input.Length - i] != count) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + } + + return count; + } + } + +} diff --git a/src/core/srcbc/crypto/paddings/TbcPadding.cs b/src/core/srcbc/crypto/paddings/TbcPadding.cs new file mode 100644 index 0000000..d929849 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/TbcPadding.cs @@ -0,0 +1,79 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + + /// A padder that adds Trailing-Bit-Compliment padding to a block. + ///

+ /// This padding pads the block out compliment of the last bit + /// of the plain text. + ///

+ ///
+ public class TbcPadding + : IBlockCipherPadding + { + /// Return the name of the algorithm the cipher implements. + /// the name of the algorithm the cipher implements. + /// + public string PaddingName + { + get { return "TBC"; } + } + + /// Initialise the padder. + /// - a SecureRandom if available. + /// + public virtual void Init(SecureRandom random) + { + // nothing to do. + } + + /// add the pad bytes to the passed in block, returning the + /// number of bytes added. + ///

+ /// Note: this assumes that the last block of plain text is always + /// passed to it inside in. i.e. if inOff is zero, indicating the + /// entire block is to be overwritten with padding the value of in + /// should be the same as the last block of plain text. + ///

+ ///
+ public virtual int AddPadding(byte[] input, int inOff) + { + int count = input.Length - inOff; + byte code; + + if (inOff > 0) + { + code = (byte)((input[inOff - 1] & 0x01) == 0?0xff:0x00); + } + else + { + code = (byte)((input[input.Length - 1] & 0x01) == 0?0xff:0x00); + } + + while (inOff < input.Length) + { + input[inOff] = code; + inOff++; + } + + return count; + } + + /// return the number of pad bytes present in the block. + public virtual int PadCount(byte[] input) + { + byte code = input[input.Length - 1]; + + int index = input.Length - 1; + while (index > 0 && input[index - 1] == code) + { + index--; + } + + return input.Length - index; + } + } +} diff --git a/src/core/srcbc/crypto/paddings/X923Padding.cs b/src/core/srcbc/crypto/paddings/X923Padding.cs new file mode 100644 index 0000000..9dc7724 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/X923Padding.cs @@ -0,0 +1,82 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * A padder that adds X9.23 padding to a block - if a SecureRandom is + * passed in random padding is assumed, otherwise padding with zeros is used. + */ + public class X923Padding + : IBlockCipherPadding + { + private SecureRandom random; + + /** + * Initialise the padder. + * + * @param random a SecureRandom if one is available. + */ + public void Init( + SecureRandom random) + { + this.random = random; + } + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public string PaddingName + { + get { return "X9.23"; } + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int AddPadding( + byte[] input, + int inOff) + { + byte code = (byte)(input.Length - inOff); + + while (inOff < input.Length - 1) + { + if (random == null) + { + input[inOff] = 0; + } + else + { + input[inOff] = (byte)random.NextInt(); + } + inOff++; + } + + input[inOff] = code; + + return code; + } + + /** + * return the number of pad bytes present in the block. + */ + public int PadCount( + byte[] input) + { + int count = input[input.Length - 1] & 0xff; + + if (count > input.Length) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + return count; + } + } +} diff --git a/src/core/srcbc/crypto/paddings/ZeroBytePadding.cs b/src/core/srcbc/crypto/paddings/ZeroBytePadding.cs new file mode 100644 index 0000000..9f9e9d2 --- /dev/null +++ b/src/core/srcbc/crypto/paddings/ZeroBytePadding.cs @@ -0,0 +1,68 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + + /// A padder that adds Null byte padding to a block. + public class ZeroBytePadding : IBlockCipherPadding + { + /// Return the name of the algorithm the cipher implements. + /// + /// + /// the name of the algorithm the cipher implements. + /// + public string PaddingName + { + get { return "ZeroBytePadding"; } + } + + /// Initialise the padder. + /// + /// + /// - a SecureRandom if available. + /// + public void Init(SecureRandom random) + { + // nothing to do. + } + + /// add the pad bytes to the passed in block, returning the + /// number of bytes added. + /// + public int AddPadding( + byte[] input, + int inOff) + { + int added = (input.Length - inOff); + + while (inOff < input.Length) + { + input[inOff] = (byte) 0; + inOff++; + } + + return added; + } + + /// return the number of pad bytes present in the block. + public int PadCount( + byte[] input) + { + int count = input.Length; + + while (count > 0) + { + if (input[count - 1] != 0) + { + break; + } + + count--; + } + + return input.Length - count; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/AEADParameters.cs b/src/core/srcbc/crypto/parameters/AEADParameters.cs new file mode 100644 index 0000000..f776c22 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/AEADParameters.cs @@ -0,0 +1,53 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class AeadParameters + : ICipherParameters + { + private readonly byte[] associatedText; + private readonly byte[] nonce; + private readonly KeyParameter key; + private readonly int macSize; + + /** + * Base constructor. + * + * @param key key to be used by underlying cipher + * @param macSize macSize in bits + * @param nonce nonce to be used + * @param associatedText associated text, if any + */ + public AeadParameters( + KeyParameter key, + int macSize, + byte[] nonce, + byte[] associatedText) + { + this.key = key; + this.nonce = nonce; + this.macSize = macSize; + this.associatedText = associatedText; + } + + public virtual KeyParameter Key + { + get { return key; } + } + + public virtual int MacSize + { + get { return macSize; } + } + + public virtual byte[] GetAssociatedText() + { + return associatedText; + } + + public virtual byte[] GetNonce() + { + return nonce; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/CcmParameters.cs b/src/core/srcbc/crypto/parameters/CcmParameters.cs new file mode 100644 index 0000000..a915778 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/CcmParameters.cs @@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class CcmParameters + : AeadParameters + { + /** + * Base constructor. + * + * @param key key to be used by underlying cipher + * @param macSize macSize in bits + * @param nonce nonce to be used + * @param associatedText associated text, if any + */ + public CcmParameters( + KeyParameter key, + int macSize, + byte[] nonce, + byte[] associatedText) + : base(key, macSize, nonce, associatedText) + { + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DHKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/DHKeyGenerationParameters.cs new file mode 100644 index 0000000..f83dfaf --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DHKeyGenerationParameters.cs @@ -0,0 +1,25 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHKeyGenerationParameters + : KeyGenerationParameters + { + private readonly DHParameters parameters; + + public DHKeyGenerationParameters( + SecureRandom random, + DHParameters parameters) + : base(random, parameters.P.BitLength) + { + this.parameters = parameters; + } + + public DHParameters Parameters + { + get { return parameters; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DHKeyParameters.cs b/src/core/srcbc/crypto/parameters/DHKeyParameters.cs new file mode 100644 index 0000000..bd82946 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DHKeyParameters.cs @@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHKeyParameters + : AsymmetricKeyParameter + { + private readonly DHParameters parameters; + + protected DHKeyParameters( + bool isPrivate, + DHParameters parameters) + : base(isPrivate) + { + // TODO Should we allow parameters to be null? + this.parameters = parameters; + } + + public DHParameters Parameters + { + get { return parameters; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHKeyParameters other = obj as DHKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHKeyParameters other) + { + return Platform.Equals(parameters, other.parameters) + && base.Equals(other); + } + + public override int GetHashCode() + { + int hc = base.GetHashCode(); + + if (parameters != null) + { + hc ^= parameters.GetHashCode(); + } + + return hc; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DHParameters.cs b/src/core/srcbc/crypto/parameters/DHParameters.cs new file mode 100644 index 0000000..575e096 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DHParameters.cs @@ -0,0 +1,178 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHParameters + : ICipherParameters + { + private const int DefaultMinimumLength = 160; + + private readonly BigInteger p, g, q, j; + private readonly int m, l; + private readonly DHValidationParameters validation; + + private static int GetDefaultM( + BigInteger p, + int l) + { + int effectiveL = l != 0 ? l : p.BitLength - 1; + + return System.Math.Min(DefaultMinimumLength, effectiveL); + +// return DefaultMinimumLength; + } + + public DHParameters( + BigInteger p, + BigInteger g) + : this(p, g, null, GetDefaultM(p, 0), 0, null, null) + { + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q) + : this(p, g, q, GetDefaultM(p, 0), 0, null, null) + { + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + int l) + : this(p, g, q, GetDefaultM(p, l), l, null, null) + { + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + int m, + int l) + : this(p, g, q, m, l, null, null) + { + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + BigInteger j, + DHValidationParameters validation) + : this(p, g, q, GetDefaultM(p, 0), 0, j, validation) + { + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + int m, + int l, + BigInteger j, + DHValidationParameters validation) + { + if (p == null) + throw new ArgumentNullException("p"); + if (g == null) + throw new ArgumentNullException("g"); + if (!p.TestBit(0)) + throw new ArgumentException("field must be an odd prime", "p"); + if (g.CompareTo(BigInteger.Two) < 0 + || g.CompareTo(p.Subtract(BigInteger.Two)) > 0) + throw new ArgumentException("generator must in the range [2, p - 2]", "g"); + if (m >= p.BitLength) + throw new ArgumentException("m value must be < bitlength of p", "m"); + if (l != 0 && l < m) + throw new ArgumentException("l value must be >= m, or zero", "l"); + if (j != null && j.CompareTo(BigInteger.Two) < 0) + throw new ArgumentException("subgroup factor must be >= 2", "j"); + + this.p = p; + this.g = g; + this.q = q; + this.m = m; + this.l = l; + this.j = j; + this.validation = validation; + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger G + { + get { return g; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger J + { + get { return j; } + } + + /// The minimum bitlength of the private value. + public int M + { + get { return m; } + } + + /// The bitlength of the private value. + /// If zero, bitLength(p) - 1 will be used. + public int L + { + get { return l; } + } + + public DHValidationParameters ValidationParameters + { + get { return validation; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHParameters other = obj as DHParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHParameters other) + { + return p.Equals(other.p) + && g.Equals(other.g) + && Platform.Equals(q, other.q); + } + + public override int GetHashCode() + { + int hc = p.GetHashCode() ^ g.GetHashCode(); + + if (q != null) + { + hc ^= q.GetHashCode(); + } + + return hc; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DHPrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/DHPrivateKeyParameters.cs new file mode 100644 index 0000000..1932ac0 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DHPrivateKeyParameters.cs @@ -0,0 +1,50 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHPrivateKeyParameters + : DHKeyParameters + { + private readonly BigInteger x; + + public DHPrivateKeyParameters( + BigInteger x, + DHParameters parameters) + : base(true, parameters) + { + this.x = x; + } + + public BigInteger X + { + get { return x; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHPrivateKeyParameters other = obj as DHPrivateKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHPrivateKeyParameters other) + { + return x.Equals(other.x) && base.Equals(other); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DHPublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/DHPublicKeyParameters.cs new file mode 100644 index 0000000..d081841 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DHPublicKeyParameters.cs @@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHPublicKeyParameters + : DHKeyParameters + { + private readonly BigInteger y; + + public DHPublicKeyParameters( + BigInteger y, + DHParameters parameters) + : base(false, parameters) + { + if (y == null) + throw new ArgumentNullException("y"); + + this.y = y; + } + + public BigInteger Y + { + get { return y; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHPublicKeyParameters other = obj as DHPublicKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHPublicKeyParameters other) + { + return y.Equals(other.y) && base.Equals(other); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DHValidationParameters.cs b/src/core/srcbc/crypto/parameters/DHValidationParameters.cs new file mode 100644 index 0000000..cdd883b --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DHValidationParameters.cs @@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHValidationParameters + { + private readonly byte[] seed; + private readonly int counter; + + public DHValidationParameters( + byte[] seed, + int counter) + { + if (seed == null) + throw new ArgumentNullException("seed"); + + this.seed = (byte[]) seed.Clone(); + this.counter = counter; + } + + public byte[] GetSeed() + { + return (byte[]) seed.Clone(); + } + + public int Counter + { + get { return counter; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHValidationParameters other = obj as DHValidationParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHValidationParameters other) + { + return counter == other.counter + && Arrays.AreEqual(this.seed, other.seed); + } + + public override int GetHashCode() + { + return counter.GetHashCode() ^ Arrays.GetHashCode(seed); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DesEdeParameters.cs b/src/core/srcbc/crypto/parameters/DesEdeParameters.cs new file mode 100644 index 0000000..20991c9 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DesEdeParameters.cs @@ -0,0 +1,95 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DesEdeParameters + : DesParameters + { + /* + * DES-EDE Key length in bytes. + */ + public const int DesEdeKeyLength = 24; + + private static byte[] FixKey( + byte[] key, + int keyOff, + int keyLen) + { + byte[] tmp = new byte[24]; + + switch (keyLen) + { + case 16: + Array.Copy(key, keyOff, tmp, 0, 16); + Array.Copy(key, keyOff, tmp, 16, 8); + break; + case 24: + Array.Copy(key, keyOff, tmp, 0, 24); + break; + default: + throw new ArgumentException("Bad length for DESede key: " + keyLen, "keyLen"); + } + + if (IsWeakKey(tmp)) + throw new ArgumentException("attempt to create weak DESede key"); + + return tmp; + } + + public DesEdeParameters( + byte[] key) + : base(FixKey(key, 0, key.Length)) + { + } + + public DesEdeParameters( + byte[] key, + int keyOff, + int keyLen) + : base(FixKey(key, keyOff, keyLen)) + { + } + + /** + * return true if the passed in key is a DES-EDE weak key. + * + * @param key bytes making up the key + * @param offset offset into the byte array the key starts at + * @param length number of bytes making up the key + */ + public static bool IsWeakKey( + byte[] key, + int offset, + int length) + { + for (int i = offset; i < length; i += DesKeyLength) + { + if (DesParameters.IsWeakKey(key, i)) + { + return true; + } + } + + return false; + } + + /** + * return true if the passed in key is a DES-EDE weak key. + * + * @param key bytes making up the key + * @param offset offset into the byte array the key starts at + */ + public static new bool IsWeakKey( + byte[] key, + int offset) + { + return IsWeakKey(key, offset, key.Length - offset); + } + + public static new bool IsWeakKey( + byte[] key) + { + return IsWeakKey(key, 0, key.Length); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DesParameters.cs b/src/core/srcbc/crypto/parameters/DesParameters.cs new file mode 100644 index 0000000..66d6433 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DesParameters.cs @@ -0,0 +1,130 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DesParameters + : KeyParameter + { + public DesParameters( + byte[] key) + : base(key) + { + if (IsWeakKey(key)) + throw new ArgumentException("attempt to create weak DES key"); + } + + public DesParameters( + byte[] key, + int keyOff, + int keyLen) + : base(key, keyOff, keyLen) + { + if (IsWeakKey(key, keyOff)) + throw new ArgumentException("attempt to create weak DES key"); + } + + /* + * DES Key Length in bytes. + */ + public const int DesKeyLength = 8; + + /* + * Table of weak and semi-weak keys taken from Schneier pp281 + */ + private const int N_DES_WEAK_KEYS = 16; + + private static readonly byte[] DES_weak_keys = + { + /* weak keys */ + (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 + }; + + /** + * DES has 16 weak keys. This method will check + * if the given DES key material is weak or semi-weak. + * Key material that is too short is regarded as weak. + *

+ * See "Applied + * Cryptography" by Bruce Schneier for more information. + *

+ * @return true if the given DES key material is weak or semi-weak, + * false otherwise. + */ + public static bool IsWeakKey( + byte[] key, + int offset) + { + if (key.Length - offset < DesKeyLength) + throw new ArgumentException("key material too short."); + + //nextkey: + for (int i = 0; i < N_DES_WEAK_KEYS; i++) + { + bool unmatch = false; + for (int j = 0; j < DesKeyLength; j++) + { + if (key[j + offset] != DES_weak_keys[i * DesKeyLength + j]) + { + //continue nextkey; + unmatch = true; + break; + } + } + + if (!unmatch) + { + return true; + } + } + + return false; + } + + public static bool IsWeakKey( + byte[] key) + { + return IsWeakKey(key, 0); + } + + /** + * DES Keys use the LSB as the odd parity bit. This can + * be used to check for corrupt keys. + * + * @param bytes the byte array to set the parity on. + */ + public static void SetOddParity( + byte[] bytes) + { + for (int i = 0; i < bytes.Length; i++) + { + int b = bytes[i]; + bytes[i] = (byte)((b & 0xfe) | + ((((b >> 1) ^ + (b >> 2) ^ + (b >> 3) ^ + (b >> 4) ^ + (b >> 5) ^ + (b >> 6) ^ + (b >> 7)) ^ 0x01) & 0x01)); + } + } + } + +} diff --git a/src/core/srcbc/crypto/parameters/DsaKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/DsaKeyGenerationParameters.cs new file mode 100644 index 0000000..622ac7b --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DsaKeyGenerationParameters.cs @@ -0,0 +1,26 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaKeyGenerationParameters + : KeyGenerationParameters + { + private readonly DsaParameters parameters; + + public DsaKeyGenerationParameters( + SecureRandom random, + DsaParameters parameters) + : base(random, parameters.P.BitLength - 1) + { + this.parameters = parameters; + } + + public DsaParameters Parameters + { + get { return parameters; } + } + } + +} diff --git a/src/core/srcbc/crypto/parameters/DsaKeyParameters.cs b/src/core/srcbc/crypto/parameters/DsaKeyParameters.cs new file mode 100644 index 0000000..8b5d508 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DsaKeyParameters.cs @@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaKeyParameters + : AsymmetricKeyParameter + { + private readonly DsaParameters parameters; + + public DsaKeyParameters( + bool isPrivate, + DsaParameters parameters) + : base(isPrivate) + { + // Note: parameters may be null + this.parameters = parameters; + } + + public DsaParameters Parameters + { + get { return parameters; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DsaKeyParameters other = obj as DsaKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaKeyParameters other) + { + return Platform.Equals(parameters, other.parameters) + && base.Equals(other); + } + + public override int GetHashCode() + { + int hc = base.GetHashCode(); + + if (parameters != null) + { + hc ^= parameters.GetHashCode(); + } + + return hc; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DsaParameters.cs b/src/core/srcbc/crypto/parameters/DsaParameters.cs new file mode 100644 index 0000000..ec349ba --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DsaParameters.cs @@ -0,0 +1,85 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaParameters + : ICipherParameters + { + private readonly BigInteger p, q , g; + private readonly DsaValidationParameters validation; + + public DsaParameters( + BigInteger p, + BigInteger q, + BigInteger g) + : this(p, q, g, null) + { + } + + public DsaParameters( + BigInteger p, + BigInteger q, + BigInteger g, + DsaValidationParameters parameters) + { + if (p == null) + throw new ArgumentNullException("p"); + if (q == null) + throw new ArgumentNullException("q"); + if (g == null) + throw new ArgumentNullException("g"); + + this.p = p; + this.q = q; + this.g = g; + this.validation = parameters; + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger G + { + get { return g; } + } + + public DsaValidationParameters ValidationParameters + { + get { return validation; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DsaParameters other = obj as DsaParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaParameters other) + { + return p.Equals(other.p) && q.Equals(other.q) && g.Equals(other.g); + } + + public override int GetHashCode() + { + return p.GetHashCode() ^ q.GetHashCode() ^ g.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DsaPrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/DsaPrivateKeyParameters.cs new file mode 100644 index 0000000..3061b70 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DsaPrivateKeyParameters.cs @@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaPrivateKeyParameters + : DsaKeyParameters + { + private readonly BigInteger x; + + public DsaPrivateKeyParameters( + BigInteger x, + DsaParameters parameters) + : base(true, parameters) + { + if (x == null) + throw new ArgumentNullException("x"); + + this.x = x; + } + + public BigInteger X + { + get { return x; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DsaPrivateKeyParameters other = obj as DsaPrivateKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaPrivateKeyParameters other) + { + return x.Equals(other.x) && base.Equals(other); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DsaPublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/DsaPublicKeyParameters.cs new file mode 100644 index 0000000..5334c34 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DsaPublicKeyParameters.cs @@ -0,0 +1,52 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaPublicKeyParameters + : DsaKeyParameters + { + private readonly BigInteger y; + + public DsaPublicKeyParameters( + BigInteger y, + DsaParameters parameters) + : base(false, parameters) + { + if (y == null) + throw new ArgumentNullException("y"); + + this.y = y; + } + + public BigInteger Y + { + get { return y; } + } + + public override bool Equals(object obj) + { + if (obj == this) + return true; + + DsaPublicKeyParameters other = obj as DsaPublicKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaPublicKeyParameters other) + { + return y.Equals(other.y) && base.Equals(other); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/DsaValidationParameters.cs b/src/core/srcbc/crypto/parameters/DsaValidationParameters.cs new file mode 100644 index 0000000..635eae1 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/DsaValidationParameters.cs @@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaValidationParameters + { + private readonly byte[] seed; + private readonly int counter; + + public DsaValidationParameters( + byte[] seed, + int counter) + { + if (seed == null) + throw new ArgumentNullException("seed"); + + this.seed = (byte[]) seed.Clone(); + this.counter = counter; + } + + public byte[] GetSeed() + { + return (byte[]) seed.Clone(); + } + + public int Counter + { + get { return counter; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DsaValidationParameters other = obj as DsaValidationParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaValidationParameters other) + { + return counter == other.counter + && Arrays.AreEqual(seed, other.seed); + } + + public override int GetHashCode() + { + return counter.GetHashCode() ^ Arrays.GetHashCode(seed); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ECDomainParameters.cs b/src/core/srcbc/crypto/parameters/ECDomainParameters.cs new file mode 100644 index 0000000..aaedaf5 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECDomainParameters.cs @@ -0,0 +1,116 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECDomainParameters + { + internal ECCurve curve; + internal byte[] seed; + internal ECPoint g; + internal BigInteger n; + internal BigInteger h; + + public ECDomainParameters( + ECCurve curve, + ECPoint g, + BigInteger n) + : this(curve, g, n, BigInteger.One) + { + } + + public ECDomainParameters( + ECCurve curve, + ECPoint g, + BigInteger n, + BigInteger h) + : this(curve, g, n, h, null) + { + } + + public ECDomainParameters( + ECCurve curve, + ECPoint g, + BigInteger n, + BigInteger h, + byte[] seed) + { + if (curve == null) + throw new ArgumentNullException("curve"); + if (g == null) + throw new ArgumentNullException("g"); + if (n == null) + throw new ArgumentNullException("n"); + if (h == null) + throw new ArgumentNullException("h"); + + this.curve = curve; + this.g = g; + this.n = n; + this.h = h; + this.seed = Arrays.Clone(seed); + } + + public ECCurve Curve + { + get { return curve; } + } + + public ECPoint G + { + get { return g; } + } + + public BigInteger N + { + get { return n; } + } + + public BigInteger H + { + get { return h; } + } + + public byte[] GetSeed() + { + return Arrays.Clone(seed); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECDomainParameters other = obj as ECDomainParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECDomainParameters other) + { + return curve.Equals(other.curve) + && g.Equals(other.g) + && n.Equals(other.n) + && h.Equals(other.h) + && Arrays.AreEqual(seed, other.seed); + } + + public override int GetHashCode() + { + return curve.GetHashCode() + ^ g.GetHashCode() + ^ n.GetHashCode() + ^ h.GetHashCode() + ^ Arrays.GetHashCode(seed); + } + } + +} diff --git a/src/core/srcbc/crypto/parameters/ECKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/ECKeyGenerationParameters.cs new file mode 100644 index 0000000..e55eaac --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECKeyGenerationParameters.cs @@ -0,0 +1,55 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECKeyGenerationParameters + : KeyGenerationParameters + { + private readonly ECDomainParameters domainParams; + private readonly DerObjectIdentifier publicKeyParamSet; + + public ECKeyGenerationParameters( + ECDomainParameters domainParameters, + SecureRandom random) + : base(random, domainParameters.N.BitLength) + { + this.domainParams = domainParameters; + } + + public ECKeyGenerationParameters( + DerObjectIdentifier publicKeyParamSet, + SecureRandom random) + : this(LookupParameters(publicKeyParamSet), random) + { + this.publicKeyParamSet = publicKeyParamSet; + } + + public ECDomainParameters DomainParameters + { + get { return domainParams; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + private static ECDomainParameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + ECDomainParameters p = ECGost3410NamedCurves.GetByOid(publicKeyParamSet); + + if (p == null) + throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet"); + + return p; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ECKeyParameters.cs b/src/core/srcbc/crypto/parameters/ECKeyParameters.cs new file mode 100644 index 0000000..9dab882 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECKeyParameters.cs @@ -0,0 +1,121 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public abstract class ECKeyParameters + : AsymmetricKeyParameter + { + private readonly string algorithm; + private readonly ECDomainParameters parameters; + private readonly DerObjectIdentifier publicKeyParamSet; + + protected ECKeyParameters( + string algorithm, + bool isPrivate, + ECDomainParameters parameters) + : base(isPrivate) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (parameters == null) + throw new ArgumentNullException("parameters"); + + this.algorithm = VerifyAlgorithmName(algorithm); + this.parameters = parameters; + } + + protected ECKeyParameters( + string algorithm, + bool isPrivate, + DerObjectIdentifier publicKeyParamSet) + : base(isPrivate) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + this.algorithm = VerifyAlgorithmName(algorithm); + this.parameters = LookupParameters(publicKeyParamSet); + this.publicKeyParamSet = publicKeyParamSet; + } + + public string AlgorithmName + { + get { return algorithm; } + } + + public ECDomainParameters Parameters + { + get { return parameters; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECDomainParameters other = obj as ECDomainParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECKeyParameters other) + { + return parameters.Equals(other.parameters) && base.Equals(other); + } + + public override int GetHashCode() + { + return parameters.GetHashCode() ^ base.GetHashCode(); + } + + private string VerifyAlgorithmName( + string algorithm) + { + string upper = algorithm.ToUpper(CultureInfo.InvariantCulture); + + switch (upper) + { + case "EC": + case "ECDSA": + case "ECGOST3410": + case "ECDH": + case "ECDHC": + break; + default: + throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm"); + } + + return upper; + } + + private static ECDomainParameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + ECDomainParameters p = ECGost3410NamedCurves.GetByOid(publicKeyParamSet); + + if (p == null) + throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet"); + + return p; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ECPrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/ECPrivateKeyParameters.cs new file mode 100644 index 0000000..53f1861 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECPrivateKeyParameters.cs @@ -0,0 +1,74 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECPrivateKeyParameters + : ECKeyParameters + { + private readonly BigInteger d; + + public ECPrivateKeyParameters( + BigInteger d, + ECDomainParameters parameters) + : this("EC", d, parameters) + { + } + + public ECPrivateKeyParameters( + BigInteger d, + DerObjectIdentifier publicKeyParamSet) + : base("ECGOST3410", true, publicKeyParamSet) + { + if (d == null) + throw new ArgumentNullException("d"); + + this.d = d; + } + + public ECPrivateKeyParameters( + string algorithm, + BigInteger d, + ECDomainParameters parameters) + : base(algorithm, true, parameters) + { + if (d == null) + throw new ArgumentNullException("d"); + + this.d = d; + } + + public BigInteger D + { + get { return d; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECPrivateKeyParameters other = obj as ECPrivateKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECPrivateKeyParameters other) + { + return d.Equals(other.d) && base.Equals(other); + } + + public override int GetHashCode() + { + return d.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ECPublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/ECPublicKeyParameters.cs new file mode 100644 index 0000000..48dfd0c --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ECPublicKeyParameters.cs @@ -0,0 +1,73 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECPublicKeyParameters + : ECKeyParameters + { + private readonly ECPoint q; + + public ECPublicKeyParameters( + ECPoint q, + ECDomainParameters parameters) + : this("EC", q, parameters) + { + } + + public ECPublicKeyParameters( + ECPoint q, + DerObjectIdentifier publicKeyParamSet) + : base("ECGOST3410", false, publicKeyParamSet) + { + if (q == null) + throw new ArgumentNullException("q"); + + this.q = q; + } + + public ECPublicKeyParameters( + string algorithm, + ECPoint q, + ECDomainParameters parameters) + : base(algorithm, false, parameters) + { + if (q == null) + throw new ArgumentNullException("q"); + + this.q = q; + } + + public ECPoint Q + { + get { return q; } + } + + public override bool Equals(object obj) + { + if (obj == this) + return true; + + ECPublicKeyParameters other = obj as ECPublicKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECPublicKeyParameters other) + { + return q.Equals(other.q) && base.Equals(other); + } + + public override int GetHashCode() + { + return q.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ElGamalKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalKeyGenerationParameters.cs new file mode 100644 index 0000000..fb00bf6 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ElGamalKeyGenerationParameters.cs @@ -0,0 +1,25 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalKeyGenerationParameters + : KeyGenerationParameters + { + private readonly ElGamalParameters parameters; + + public ElGamalKeyGenerationParameters( + SecureRandom random, + ElGamalParameters parameters) + : base(random, parameters.P.BitLength) + { + this.parameters = parameters; + } + + public ElGamalParameters Parameters + { + get { return parameters; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ElGamalKeyParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalKeyParameters.cs new file mode 100644 index 0000000..d545a98 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ElGamalKeyParameters.cs @@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalKeyParameters + : AsymmetricKeyParameter + { + private readonly ElGamalParameters parameters; + + protected ElGamalKeyParameters( + bool isPrivate, + ElGamalParameters parameters) + : base(isPrivate) + { + // TODO Should we allow 'parameters' to be null? + this.parameters = parameters; + } + + public ElGamalParameters Parameters + { + get { return parameters; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ElGamalKeyParameters other = obj as ElGamalKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ElGamalKeyParameters other) + { + return Platform.Equals(parameters, other.parameters) + && base.Equals(other); + } + + public override int GetHashCode() + { + int hc = base.GetHashCode(); + + if (parameters != null) + { + hc ^= parameters.GetHashCode(); + } + + return hc; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ElGamalParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalParameters.cs new file mode 100644 index 0000000..9e7799b --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ElGamalParameters.cs @@ -0,0 +1,81 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalParameters + : ICipherParameters + { + private readonly BigInteger p, g; + private readonly int l; + + public ElGamalParameters( + BigInteger p, + BigInteger g) + : this(p, g, 0) + { + } + + public ElGamalParameters( + BigInteger p, + BigInteger g, + int l) + { + if (p == null) + throw new ArgumentNullException("p"); + if (g == null) + throw new ArgumentNullException("g"); + + this.p = p; + this.g = g; + this.l = l; + } + + public BigInteger P + { + get { return p; } + } + + /** + * return the generator - g + */ + public BigInteger G + { + get { return g; } + } + + /** + * return private value limit - l + */ + public int L + { + get { return l; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ElGamalParameters other = obj as ElGamalParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ElGamalParameters other) + { + return p.Equals(other.p) && g.Equals(other.g) && l == other.l; + } + + public override int GetHashCode() + { + return p.GetHashCode() ^ g.GetHashCode() ^ l; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ElGamalPrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalPrivateKeyParameters.cs new file mode 100644 index 0000000..73dbd8e --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ElGamalPrivateKeyParameters.cs @@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalPrivateKeyParameters + : ElGamalKeyParameters + { + private readonly BigInteger x; + + public ElGamalPrivateKeyParameters( + BigInteger x, + ElGamalParameters parameters) + : base(true, parameters) + { + if (x == null) + throw new ArgumentNullException("x"); + + this.x = x; + } + + public BigInteger X + { + get { return x; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ElGamalPrivateKeyParameters other = obj as ElGamalPrivateKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ElGamalPrivateKeyParameters other) + { + return other.x.Equals(x) && base.Equals(other); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ElGamalPublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/ElGamalPublicKeyParameters.cs new file mode 100644 index 0000000..799f293 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ElGamalPublicKeyParameters.cs @@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalPublicKeyParameters + : ElGamalKeyParameters + { + private readonly BigInteger y; + + public ElGamalPublicKeyParameters( + BigInteger y, + ElGamalParameters parameters) + : base(false, parameters) + { + if (y == null) + throw new ArgumentNullException("y"); + + this.y = y; + } + + public BigInteger Y + { + get { return y; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ElGamalPublicKeyParameters other = obj as ElGamalPublicKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ElGamalPublicKeyParameters other) + { + return y.Equals(other.y) && base.Equals(other); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/GOST3410KeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410KeyGenerationParameters.cs new file mode 100644 index 0000000..3343717 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/GOST3410KeyGenerationParameters.cs @@ -0,0 +1,55 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410KeyGenerationParameters + : KeyGenerationParameters + { + private readonly Gost3410Parameters parameters; + private readonly DerObjectIdentifier publicKeyParamSet; + + public Gost3410KeyGenerationParameters( + SecureRandom random, + Gost3410Parameters parameters) + : base(random, parameters.P.BitLength - 1) + { + this.parameters = parameters; + } + + public Gost3410KeyGenerationParameters( + SecureRandom random, + DerObjectIdentifier publicKeyParamSet) + : this(random, LookupParameters(publicKeyParamSet)) + { + this.publicKeyParamSet = publicKeyParamSet; + } + + public Gost3410Parameters Parameters + { + get { return parameters; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + private static Gost3410Parameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + Gost3410ParamSetParameters p = Gost3410NamedParameters.GetByOid(publicKeyParamSet); + + if (p == null) + throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet"); + + return new Gost3410Parameters(p.P, p.Q, p.A); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/GOST3410KeyParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410KeyParameters.cs new file mode 100644 index 0000000..98b5767 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/GOST3410KeyParameters.cs @@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public abstract class Gost3410KeyParameters + : AsymmetricKeyParameter + { + private readonly Gost3410Parameters parameters; + private readonly DerObjectIdentifier publicKeyParamSet; + + protected Gost3410KeyParameters( + bool isPrivate, + Gost3410Parameters parameters) + : base(isPrivate) + { + this.parameters = parameters; + } + + protected Gost3410KeyParameters( + bool isPrivate, + DerObjectIdentifier publicKeyParamSet) + : base(isPrivate) + { + this.parameters = LookupParameters(publicKeyParamSet); + this.publicKeyParamSet = publicKeyParamSet; + } + + public Gost3410Parameters Parameters + { + get { return parameters; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + // TODO Implement Equals/GetHashCode + + private static Gost3410Parameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + Gost3410ParamSetParameters p = Gost3410NamedParameters.GetByOid(publicKeyParamSet); + + if (p == null) + throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet"); + + return new Gost3410Parameters(p.P, p.Q, p.A); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/GOST3410Parameters.cs b/src/core/srcbc/crypto/parameters/GOST3410Parameters.cs new file mode 100644 index 0000000..d4b4029 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/GOST3410Parameters.cs @@ -0,0 +1,86 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410Parameters + : ICipherParameters + { + private readonly BigInteger p, q, a; + private readonly Gost3410ValidationParameters validation; + + public Gost3410Parameters( + BigInteger p, + BigInteger q, + BigInteger a) + : this(p, q, a, null) + { + } + + public Gost3410Parameters( + BigInteger p, + BigInteger q, + BigInteger a, + Gost3410ValidationParameters validation) + { + if (p == null) + throw new ArgumentNullException("p"); + if (q == null) + throw new ArgumentNullException("q"); + if (a == null) + throw new ArgumentNullException("a"); + + this.p = p; + this.q = q; + this.a = a; + this.validation = validation; + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger A + { + get { return a; } + } + + public Gost3410ValidationParameters ValidationParameters + { + get { return validation; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + Gost3410Parameters other = obj as Gost3410Parameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + Gost3410Parameters other) + { + return p.Equals(other.p) && q.Equals(other.q) && a.Equals(other.a); + } + + public override int GetHashCode() + { + return p.GetHashCode() ^ q.GetHashCode() ^ a.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/GOST3410PrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410PrivateKeyParameters.cs new file mode 100644 index 0000000..6348c36 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/GOST3410PrivateKeyParameters.cs @@ -0,0 +1,41 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410PrivateKeyParameters + : Gost3410KeyParameters + { + private readonly BigInteger x; + + public Gost3410PrivateKeyParameters( + BigInteger x, + Gost3410Parameters parameters) + : base(true, parameters) + { + if (x.SignValue < 1 || x.BitLength > 256 || x.CompareTo(Parameters.Q) >= 0) + throw new ArgumentException("Invalid x for GOST3410 private key", "x"); + + this.x = x; + } + + public Gost3410PrivateKeyParameters( + BigInteger x, + DerObjectIdentifier publicKeyParamSet) + : base(true, publicKeyParamSet) + { + if (x.SignValue < 1 || x.BitLength > 256 || x.CompareTo(Parameters.Q) >= 0) + throw new ArgumentException("Invalid x for GOST3410 private key", "x"); + + this.x = x; + } + + public BigInteger X + { + get { return x; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/GOST3410PublicKeyParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410PublicKeyParameters.cs new file mode 100644 index 0000000..7f3286b --- /dev/null +++ b/src/core/srcbc/crypto/parameters/GOST3410PublicKeyParameters.cs @@ -0,0 +1,40 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410PublicKeyParameters + : Gost3410KeyParameters + { + private readonly BigInteger y; + + public Gost3410PublicKeyParameters( + BigInteger y, + Gost3410Parameters parameters) + : base(false, parameters) + { + if (y.SignValue < 1 || y.CompareTo(Parameters.P) >= 0) + throw new ArgumentException("Invalid y for GOST3410 public key", "y"); + + this.y = y; + } + + public Gost3410PublicKeyParameters( + BigInteger y, + DerObjectIdentifier publicKeyParamSet) + : base(false, publicKeyParamSet) + { + if (y.SignValue < 1 || y.CompareTo(Parameters.P) >= 0) + throw new ArgumentException("Invalid y for GOST3410 public key", "y"); + + this.y = y; + } + + public BigInteger Y + { + get { return y; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/GOST3410ValidationParameters.cs b/src/core/srcbc/crypto/parameters/GOST3410ValidationParameters.cs new file mode 100644 index 0000000..6ff41c5 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/GOST3410ValidationParameters.cs @@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410ValidationParameters + { + private int x0; + private int c; + private long x0L; + private long cL; + + public Gost3410ValidationParameters( + int x0, + int c) + { + this.x0 = x0; + this.c = c; + } + + public Gost3410ValidationParameters( + long x0L, + long cL) + { + this.x0L = x0L; + this.cL = cL; + } + + public int C { get { return c; } } + public int X0 { get { return x0; } } + public long CL { get { return cL; } } + public long X0L { get { return x0L; } } + + public override bool Equals( + object obj) + { + Gost3410ValidationParameters other = obj as Gost3410ValidationParameters; + + return other != null + && other.c == this.c + && other.x0 == this.x0 + && other.cL == this.cL + && other.x0L == this.x0L; + } + + public override int GetHashCode() + { + return c.GetHashCode() ^ x0.GetHashCode() ^ cL.GetHashCode() ^ x0L.GetHashCode(); + } + + } +} diff --git a/src/core/srcbc/crypto/parameters/ISO18033KDFParameters.cs b/src/core/srcbc/crypto/parameters/ISO18033KDFParameters.cs new file mode 100644 index 0000000..e82dc99 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ISO18033KDFParameters.cs @@ -0,0 +1,25 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * parameters for Key derivation functions for ISO-18033 + */ + public class Iso18033KdfParameters + : IDerivationParameters + { + byte[] seed; + + public Iso18033KdfParameters( + byte[] seed) + { + this.seed = seed; + } + + public byte[] GetSeed() + { + return seed; + } + } +} diff --git a/src/core/srcbc/crypto/parameters/IesParameters.cs b/src/core/srcbc/crypto/parameters/IesParameters.cs new file mode 100644 index 0000000..eee6be0 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/IesParameters.cs @@ -0,0 +1,49 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * parameters for using an integrated cipher in stream mode. + */ + public class IesParameters : ICipherParameters + { + private byte[] derivation; + private byte[] encoding; + private int macKeySize; + + /** + * @param derivation the derivation parameter for the KDF function. + * @param encoding the encoding parameter for the KDF function. + * @param macKeySize the size of the MAC key (in bits). + */ + public IesParameters( + byte[] derivation, + byte[] encoding, + int macKeySize) + { + this.derivation = derivation; + this.encoding = encoding; + this.macKeySize = macKeySize; + } + + public byte[] GetDerivationV() + { + return derivation; + } + + public byte[] GetEncodingV() + { + return encoding; + } + + public int MacKeySize + { + get + { + return macKeySize; + } + } + } + +} diff --git a/src/core/srcbc/crypto/parameters/IesWithCipherParameters.cs b/src/core/srcbc/crypto/parameters/IesWithCipherParameters.cs new file mode 100644 index 0000000..b2f8804 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/IesWithCipherParameters.cs @@ -0,0 +1,33 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class IesWithCipherParameters : IesParameters + { + private int cipherKeySize; + + /** + * @param derivation the derivation parameter for the KDF function. + * @param encoding the encoding parameter for the KDF function. + * @param macKeySize the size of the MAC key (in bits). + * @param cipherKeySize the size of the associated Cipher key (in bits). + */ + public IesWithCipherParameters( + byte[] derivation, + byte[] encoding, + int macKeySize, + int cipherKeySize) : base(derivation, encoding, macKeySize) + { + this.cipherKeySize = cipherKeySize; + } + + public int CipherKeySize + { + get + { + return cipherKeySize; + } + } + } + +} diff --git a/src/core/srcbc/crypto/parameters/KdfParameters.cs b/src/core/srcbc/crypto/parameters/KdfParameters.cs new file mode 100644 index 0000000..7eb4c09 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/KdfParameters.cs @@ -0,0 +1,33 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * parameters for Key derivation functions for IEEE P1363a + */ + public class KdfParameters : IDerivationParameters + { + byte[] iv; + byte[] shared; + + public KdfParameters( + byte[] shared, + byte[] iv) + { + this.shared = shared; + this.iv = iv; + } + + public byte[] GetSharedSecret() + { + return shared; + } + + public byte[] GetIV() + { + return iv; + } + } + +} diff --git a/src/core/srcbc/crypto/parameters/KeyParameter.cs b/src/core/srcbc/crypto/parameters/KeyParameter.cs new file mode 100644 index 0000000..96b5708 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/KeyParameter.cs @@ -0,0 +1,43 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class KeyParameter + : ICipherParameters + { + private readonly byte[] key; + + public KeyParameter( + byte[] key) + { + if (key == null) + throw new ArgumentNullException("key"); + + this.key = (byte[]) key.Clone(); + } + + public KeyParameter( + byte[] key, + int keyOff, + int keyLen) + { + if (key == null) + throw new ArgumentNullException("key"); + if (keyOff < 0 || keyOff > key.Length) + throw new ArgumentOutOfRangeException("keyOff"); + if (keyLen < 0 || (keyOff + keyLen) > key.Length) + throw new ArgumentOutOfRangeException("keyLen"); + + this.key = new byte[keyLen]; + Array.Copy(key, keyOff, this.key, 0, keyLen); + } + + public byte[] GetKey() + { + return (byte[]) key.Clone(); + } + } + +} diff --git a/src/core/srcbc/crypto/parameters/MgfParameters.cs b/src/core/srcbc/crypto/parameters/MgfParameters.cs new file mode 100644 index 0000000..5fbc16a --- /dev/null +++ b/src/core/srcbc/crypto/parameters/MgfParameters.cs @@ -0,0 +1,31 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /// Parameters for mask derivation functions. + public class MgfParameters + : IDerivationParameters + { + private readonly byte[] seed; + + public MgfParameters( + byte[] seed) + : this(seed, 0, seed.Length) + { + } + + public MgfParameters( + byte[] seed, + int off, + int len) + { + this.seed = new byte[len]; + Array.Copy(seed, off, this.seed, 0, len); + } + + public byte[] GetSeed() + { + return (byte[]) seed.Clone(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/NaccacheSternKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/NaccacheSternKeyGenerationParameters.cs new file mode 100644 index 0000000..828592a --- /dev/null +++ b/src/core/srcbc/crypto/parameters/NaccacheSternKeyGenerationParameters.cs @@ -0,0 +1,101 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * Parameters for NaccacheStern public private key generation. For details on + * this cipher, please see + * + * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf + */ + public class NaccacheSternKeyGenerationParameters : KeyGenerationParameters + { + // private BigInteger publicExponent; + private readonly int certainty; + private readonly int countSmallPrimes; + private bool debug; + + /** + * Parameters for generating a NaccacheStern KeyPair. + * + * @param random + * The source of randomness + * @param strength + * The desired strength of the Key in Bits + * @param certainty + * the probability that the generated primes are not really prime + * as integer: 2^(-certainty) is then the probability + * @param countSmallPrimes + * How many small key factors are desired + */ + public NaccacheSternKeyGenerationParameters( + SecureRandom random, + int strength, + int certainty, + int countSmallPrimes) + : this(random, strength, certainty, countSmallPrimes, false) + { + } + + /** + * Parameters for a NaccacheStern KeyPair. + * + * @param random + * The source of randomness + * @param strength + * The desired strength of the Key in Bits + * @param certainty + * the probability that the generated primes are not really prime + * as integer: 2^(-certainty) is then the probability + * @param cntSmallPrimes + * How many small key factors are desired + * @param debug + * Turn debugging on or off (reveals secret information, use with + * caution) + */ + public NaccacheSternKeyGenerationParameters(SecureRandom random, + int strength, + int certainty, + int countSmallPrimes, + bool debug) + : base(random, strength) + { + if (countSmallPrimes % 2 == 1) + { + throw new ArgumentException("countSmallPrimes must be a multiple of 2"); + } + if (countSmallPrimes < 30) + { + throw new ArgumentException("countSmallPrimes must be >= 30 for security reasons"); + } + this.certainty = certainty; + this.countSmallPrimes = countSmallPrimes; + this.debug = debug; + } + + /** + * @return Returns the certainty. + */ + public int Certainty + { + get { return certainty; } + } + + /** + * @return Returns the countSmallPrimes. + */ + public int CountSmallPrimes + { + get { return countSmallPrimes; } + } + + public bool IsDebug + { + get { return debug; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/NaccacheSternKeyParameters.cs b/src/core/srcbc/crypto/parameters/NaccacheSternKeyParameters.cs new file mode 100644 index 0000000..4604170 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/NaccacheSternKeyParameters.cs @@ -0,0 +1,44 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * Public key parameters for NaccacheStern cipher. For details on this cipher, + * please see + * + * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf + */ + public class NaccacheSternKeyParameters : AsymmetricKeyParameter + { + private readonly BigInteger g, n; + private readonly int lowerSigmaBound; + + /** + * @param privateKey + */ + public NaccacheSternKeyParameters(bool privateKey, BigInteger g, BigInteger n, int lowerSigmaBound) + : base(privateKey) + { + this.g = g; + this.n = n; + this.lowerSigmaBound = lowerSigmaBound; + } + + /** + * @return Returns the g. + */ + public BigInteger G { get { return g; } } + + /** + * @return Returns the lowerSigmaBound. + */ + public int LowerSigmaBound { get { return lowerSigmaBound; } } + + /** + * @return Returns the n. + */ + public BigInteger Modulus { get { return n; } } + } +} diff --git a/src/core/srcbc/crypto/parameters/NaccacheSternPrivateKeyParameters.cs b/src/core/srcbc/crypto/parameters/NaccacheSternPrivateKeyParameters.cs new file mode 100644 index 0000000..c4c9419 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/NaccacheSternPrivateKeyParameters.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * Private key parameters for NaccacheStern cipher. For details on this cipher, + * please see + * + * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf + */ + public class NaccacheSternPrivateKeyParameters : NaccacheSternKeyParameters + { + private readonly BigInteger phiN; + private readonly ArrayList smallPrimes; + + /** + * Constructs a NaccacheSternPrivateKey + * + * @param g + * the public enryption parameter g + * @param n + * the public modulus n = p*q + * @param lowerSigmaBound + * the public lower sigma bound up to which data can be encrypted + * @param smallPrimes + * the small primes, of which sigma is constructed in the right + * order + * @param phi_n + * the private modulus phi(n) = (p-1)(q-1) + */ + public NaccacheSternPrivateKeyParameters( + BigInteger g, + BigInteger n, + int lowerSigmaBound, + ArrayList smallPrimes, + BigInteger phiN) + : base(true, g, n, lowerSigmaBound) + { + this.smallPrimes = smallPrimes; + this.phiN = phiN; + } + + public BigInteger PhiN + { + get { return phiN; } + } + + public ArrayList SmallPrimes + { + get { return smallPrimes; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ParametersWithIV.cs b/src/core/srcbc/crypto/parameters/ParametersWithIV.cs new file mode 100644 index 0000000..85bee1f --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ParametersWithIV.cs @@ -0,0 +1,44 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ParametersWithIV + : ICipherParameters + { + private readonly ICipherParameters parameters; + private readonly byte[] iv; + + public ParametersWithIV( + ICipherParameters parameters, + byte[] iv) + : this(parameters, iv, 0, iv.Length) + { + } + + public ParametersWithIV( + ICipherParameters parameters, + byte[] iv, + int ivOff, + int ivLen) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + if (iv == null) + throw new ArgumentNullException("iv"); + + this.parameters = parameters; + this.iv = new byte[ivLen]; + Array.Copy(iv, ivOff, this.iv, 0, ivLen); + } + + public byte[] GetIV() + { + return (byte[]) iv.Clone(); + } + + public ICipherParameters Parameters + { + get { return parameters; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ParametersWithRandom.cs b/src/core/srcbc/crypto/parameters/ParametersWithRandom.cs new file mode 100644 index 0000000..aecc30b --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ParametersWithRandom.cs @@ -0,0 +1,48 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ParametersWithRandom + : ICipherParameters + { + private readonly ICipherParameters parameters; + private readonly SecureRandom random; + + public ParametersWithRandom( + ICipherParameters parameters, + SecureRandom random) + { + if (parameters == null) + throw new ArgumentNullException("random"); + if (random == null) + throw new ArgumentNullException("random"); + + this.parameters = parameters; + this.random = random; + } + + public ParametersWithRandom( + ICipherParameters parameters) + : this(parameters, new SecureRandom()) + { + } + + [Obsolete("Use Random property instead")] + public SecureRandom GetRandom() + { + return Random; + } + + public SecureRandom Random + { + get { return random; } + } + + public ICipherParameters Parameters + { + get { return parameters; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/ParametersWithSBox.cs b/src/core/srcbc/crypto/parameters/ParametersWithSBox.cs new file mode 100644 index 0000000..2c57528 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ParametersWithSBox.cs @@ -0,0 +1,24 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ParametersWithSBox : ICipherParameters + { + private ICipherParameters parameters; + private byte[] sBox; + + public ParametersWithSBox( + ICipherParameters parameters, + byte[] sBox) + { + this.parameters = parameters; + this.sBox = sBox; + } + + public byte[] GetSBox() { return sBox; } + + public ICipherParameters Parameters { get { return parameters; } } + } +} diff --git a/src/core/srcbc/crypto/parameters/ParametersWithSalt.cs b/src/core/srcbc/crypto/parameters/ParametersWithSalt.cs new file mode 100644 index 0000000..7ada5b7 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/ParametersWithSalt.cs @@ -0,0 +1,39 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + + /// Cipher parameters with a fixed salt value associated with them. + public class ParametersWithSalt : ICipherParameters + { + private byte[] salt; + private ICipherParameters parameters; + + public ParametersWithSalt(ICipherParameters parameters, byte[] salt):this(parameters, salt, 0, salt.Length) + { + } + + public ParametersWithSalt(ICipherParameters parameters, byte[] salt, int saltOff, int saltLen) + { + this.salt = new byte[saltLen]; + this.parameters = parameters; + + Array.Copy(salt, saltOff, this.salt, 0, saltLen); + } + + public byte[] GetSalt() + { + return salt; + } + + public ICipherParameters Parameters + { + get + { + return parameters; + } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/RC2Parameters.cs b/src/core/srcbc/crypto/parameters/RC2Parameters.cs new file mode 100644 index 0000000..cb0d38b --- /dev/null +++ b/src/core/srcbc/crypto/parameters/RC2Parameters.cs @@ -0,0 +1,47 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RC2Parameters + : KeyParameter + { + private readonly int bits; + + public RC2Parameters( + byte[] key) + : this(key, (key.Length > 128) ? 1024 : (key.Length * 8)) + { + } + + public RC2Parameters( + byte[] key, + int keyOff, + int keyLen) + : this(key, keyOff, keyLen, (keyLen > 128) ? 1024 : (keyLen * 8)) + { + } + + public RC2Parameters( + byte[] key, + int bits) + : base(key) + { + this.bits = bits; + } + + public RC2Parameters( + byte[] key, + int keyOff, + int keyLen, + int bits) + : base(key, keyOff, keyLen) + { + this.bits = bits; + } + + public int EffectiveKeyBits + { + get { return bits; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/RC5Parameters.cs b/src/core/srcbc/crypto/parameters/RC5Parameters.cs new file mode 100644 index 0000000..d84c659 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/RC5Parameters.cs @@ -0,0 +1,27 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RC5Parameters + : KeyParameter + { + private readonly int rounds; + + public RC5Parameters( + byte[] key, + int rounds) + : base(key) + { + if (key.Length > 255) + throw new ArgumentException("RC5 key length can be no greater than 255"); + + this.rounds = rounds; + } + + public int Rounds + { + get { return rounds; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/RSABlindingParameters.cs b/src/core/srcbc/crypto/parameters/RSABlindingParameters.cs new file mode 100644 index 0000000..6ccda67 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/RSABlindingParameters.cs @@ -0,0 +1,34 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RsaBlindingParameters + : ICipherParameters + { + private readonly RsaKeyParameters publicKey; + private readonly BigInteger blindingFactor; + + public RsaBlindingParameters( + RsaKeyParameters publicKey, + BigInteger blindingFactor) + { + if (publicKey.IsPrivate) + throw new ArgumentException("RSA parameters should be for a public key"); + + this.publicKey = publicKey; + this.blindingFactor = blindingFactor; + } + + public RsaKeyParameters PublicKey + { + get { return publicKey; } + } + + public BigInteger BlindingFactor + { + get { return blindingFactor; } + } + } +} diff --git a/src/core/srcbc/crypto/parameters/RsaKeyGenerationParameters.cs b/src/core/srcbc/crypto/parameters/RsaKeyGenerationParameters.cs new file mode 100644 index 0000000..1a70f5d --- /dev/null +++ b/src/core/srcbc/crypto/parameters/RsaKeyGenerationParameters.cs @@ -0,0 +1,55 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RsaKeyGenerationParameters + : KeyGenerationParameters + { + private readonly BigInteger publicExponent; + private readonly int certainty; + + public RsaKeyGenerationParameters( + BigInteger publicExponent, + SecureRandom random, + int strength, + int certainty) + : base(random, strength) + { + this.publicExponent = publicExponent; + this.certainty = certainty; + } + + public BigInteger PublicExponent + { + get { return publicExponent; } + } + + public int Certainty + { + get { return certainty; } + } + + public override bool Equals( + object obj) + { + RsaKeyGenerationParameters other = obj as RsaKeyGenerationParameters; + + if (other == null) + { + return false; + } + + return certainty == other.certainty + && publicExponent.Equals(other.publicExponent); + } + + public override int GetHashCode() + { + return certainty.GetHashCode() ^ publicExponent.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/RsaKeyParameters.cs b/src/core/srcbc/crypto/parameters/RsaKeyParameters.cs new file mode 100644 index 0000000..0158747 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/RsaKeyParameters.cs @@ -0,0 +1,54 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RsaKeyParameters + : AsymmetricKeyParameter + { + private readonly BigInteger modulus; + private readonly BigInteger exponent; + + public RsaKeyParameters( + bool isPrivate, + BigInteger modulus, + BigInteger exponent) + : base(isPrivate) + { + this.modulus = modulus; + this.exponent = exponent; + } + + public BigInteger Modulus + { + get { return modulus; } + } + + public BigInteger Exponent + { + get { return exponent; } + } + + public override bool Equals( + object obj) + { + RsaKeyParameters kp = obj as RsaKeyParameters; + + if (kp == null) + { + return false; + } + + return kp.IsPrivate == this.IsPrivate + && kp.Modulus.Equals(this.modulus) + && kp.Exponent.Equals(this.exponent); + } + + public override int GetHashCode() + { + return modulus.GetHashCode() ^ exponent.GetHashCode() ^ IsPrivate.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/parameters/RsaPrivateCrtKeyParameters.cs b/src/core/srcbc/crypto/parameters/RsaPrivateCrtKeyParameters.cs new file mode 100644 index 0000000..d2e5e42 --- /dev/null +++ b/src/core/srcbc/crypto/parameters/RsaPrivateCrtKeyParameters.cs @@ -0,0 +1,89 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RsaPrivateCrtKeyParameters + : RsaKeyParameters + { + private readonly BigInteger e, p, q, dP, dQ, qInv; + + public RsaPrivateCrtKeyParameters( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger p, + BigInteger q, + BigInteger dP, + BigInteger dQ, + BigInteger qInv) + : base(true, modulus, privateExponent) + { + this.e = publicExponent; + this.p = p; + this.q = q; + this.dP = dP; + this.dQ = dQ; + this.qInv = qInv; + } + + public BigInteger PublicExponent + { + get { return e; } + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger DP + { + get { return dP; } + } + + public BigInteger DQ + { + get { return dQ; } + } + + public BigInteger QInv + { + get { return qInv; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + RsaPrivateCrtKeyParameters kp = obj as RsaPrivateCrtKeyParameters; + + if (kp == null) + return false; + + return kp.DP.Equals(dP) + && kp.DQ.Equals(dQ) + && kp.Exponent.Equals(this.Exponent) + && kp.Modulus.Equals(this.Modulus) + && kp.P.Equals(p) + && kp.Q.Equals(q) + && kp.PublicExponent.Equals(e) + && kp.QInv.Equals(qInv); + } + + public override int GetHashCode() + { + return DP.GetHashCode() ^ DQ.GetHashCode() ^ Exponent.GetHashCode() ^ Modulus.GetHashCode() + ^ P.GetHashCode() ^ Q.GetHashCode() ^ PublicExponent.GetHashCode() ^ QInv.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/crypto/prng/CryptoApiRandomGenerator.cs b/src/core/srcbc/crypto/prng/CryptoApiRandomGenerator.cs new file mode 100644 index 0000000..bc82ec0 --- /dev/null +++ b/src/core/srcbc/crypto/prng/CryptoApiRandomGenerator.cs @@ -0,0 +1,61 @@ +#if !NETCF_1_0 + +using System; +using System.Security.Cryptography; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /// + /// Uses Microsoft's RNGCryptoServiceProvider + /// + public class CryptoApiRandomGenerator + : IRandomGenerator + { + private readonly RNGCryptoServiceProvider rndProv; + + public CryptoApiRandomGenerator() + { + rndProv = new RNGCryptoServiceProvider(); + } + + #region IRandomGenerator Members + + public virtual void AddSeedMaterial(byte[] seed) + { + // We don't care about the seed + } + + public virtual void AddSeedMaterial(long seed) + { + // We don't care about the seed + } + + public virtual void NextBytes(byte[] bytes) + { + rndProv.GetBytes(bytes); + } + + public virtual void NextBytes(byte[] bytes, int start, int len) + { + if (start < 0) + throw new ArgumentException("Start offset cannot be negative", "start"); + if (bytes.Length < (start + len)) + throw new ArgumentException("Byte array too small for requested offset and length"); + + if (bytes.Length == len && start == 0) + { + NextBytes(bytes); + } + else + { + byte[] tmpBuf = new byte[len]; + rndProv.GetBytes(tmpBuf); + Array.Copy(tmpBuf, 0, bytes, start, len); + } + } + + #endregion + } +} + +#endif diff --git a/src/core/srcbc/crypto/prng/DigestRandomGenerator.cs b/src/core/srcbc/crypto/prng/DigestRandomGenerator.cs new file mode 100644 index 0000000..5f0cc75 --- /dev/null +++ b/src/core/srcbc/crypto/prng/DigestRandomGenerator.cs @@ -0,0 +1,107 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /** + * Random generation based on the digest with counter. Calling addSeedMaterial will + * always increase the entropy of the hash. + *

+ * Internal access to the digest is syncrhonized so a single one of these can be shared. + *

+ */ + public class DigestRandomGenerator + : IRandomGenerator + { + private long counter; + private IDigest digest; + private byte[] state; + + public DigestRandomGenerator( + IDigest digest) + { + this.digest = digest; + this.state = new byte[digest.GetDigestSize()]; + this.counter = 1; + } + + public void AddSeedMaterial( + byte[] inSeed) + { + lock (this) + { + DigestUpdate(inSeed); + } + } + + public void AddSeedMaterial( + long rSeed) + { + lock (this) + { + for (int i = 0; i != 8; i++) + { + DigestUpdate((byte)rSeed); +// rSeed >>>= 8; + rSeed >>= 8; + } + } + } + + public void NextBytes( + byte[] bytes) + { + NextBytes(bytes, 0, bytes.Length); + } + + public void NextBytes( + byte[] bytes, + int start, + int len) + { + lock (this) + { + int stateOff = 0; + + DigestDoFinal(state); + + int end = start + len; + for (int i = start; i < end; ++i) + { + if (stateOff == state.Length) + { + DigestUpdate(counter++); + DigestUpdate(state); + DigestDoFinal(state); + stateOff = 0; + } + bytes[i] = state[stateOff++]; + } + + DigestUpdate(counter++); + DigestUpdate(state); + } + } + + private void DigestUpdate(long seed) + { + for (int i = 0; i != 8; i++) + { + digest.Update((byte)seed); +// seed >>>= 8; + seed >>= 8; + } + } + + private void DigestUpdate(byte[] inSeed) + { + digest.BlockUpdate(inSeed, 0, inSeed.Length); + } + + private void DigestDoFinal(byte[] result) + { + digest.DoFinal(result, 0); + } + } +} diff --git a/src/core/srcbc/crypto/prng/IRandomGenerator.cs b/src/core/srcbc/crypto/prng/IRandomGenerator.cs new file mode 100644 index 0000000..36c7dff --- /dev/null +++ b/src/core/srcbc/crypto/prng/IRandomGenerator.cs @@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /// Generic interface for objects generating random bytes. + public interface IRandomGenerator + { + /// Add more seed material to the generator. + /// A byte array to be mixed into the generator's state. + void AddSeedMaterial(byte[] seed); + + /// Add more seed material to the generator. + /// A long value to be mixed into the generator's state. + void AddSeedMaterial(long seed); + + /// Fill byte array with random values. + /// Array to be filled. + void NextBytes(byte[] bytes); + + /// Fill byte array with random values. + /// Array to receive bytes. + /// Index to start filling at. + /// Length of segment to fill. + void NextBytes(byte[] bytes, int start, int len); + } +} diff --git a/src/core/srcbc/crypto/prng/ReversedWindowGenerator.cs b/src/core/srcbc/crypto/prng/ReversedWindowGenerator.cs new file mode 100644 index 0000000..ec121e2 --- /dev/null +++ b/src/core/srcbc/crypto/prng/ReversedWindowGenerator.cs @@ -0,0 +1,98 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /// + /// Takes bytes generated by an underling RandomGenerator and reverses the order in + /// each small window (of configurable size). + ///

+ /// Access to internals is synchronized so a single one of these can be shared. + ///

+ ///
+ public class ReversedWindowGenerator + : IRandomGenerator + { + private readonly IRandomGenerator generator; + + private byte[] window; + private int windowCount; + + public ReversedWindowGenerator( + IRandomGenerator generator, + int windowSize) + { + if (generator == null) + throw new ArgumentNullException("generator"); + if (windowSize < 2) + throw new ArgumentException("Window size must be at least 2", "windowSize"); + + this.generator = generator; + this.window = new byte[windowSize]; + } + + /// Add more seed material to the generator. + /// A byte array to be mixed into the generator's state. + public virtual void AddSeedMaterial( + byte[] seed) + { + lock (this) + { + windowCount = 0; + generator.AddSeedMaterial(seed); + } + } + + /// Add more seed material to the generator. + /// A long value to be mixed into the generator's state. + public virtual void AddSeedMaterial( + long seed) + { + lock (this) + { + windowCount = 0; + generator.AddSeedMaterial(seed); + } + } + + /// Fill byte array with random values. + /// Array to be filled. + public virtual void NextBytes( + byte[] bytes) + { + doNextBytes(bytes, 0, bytes.Length); + } + + /// Fill byte array with random values. + /// Array to receive bytes. + /// Index to start filling at. + /// Length of segment to fill. + public virtual void NextBytes( + byte[] bytes, + int start, + int len) + { + doNextBytes(bytes, start, len); + } + + private void doNextBytes( + byte[] bytes, + int start, + int len) + { + lock (this) + { + int done = 0; + while (done < len) + { + if (windowCount < 1) + { + generator.NextBytes(window, 0, window.Length); + windowCount = window.Length; + } + + bytes[start + done++] = window[--windowCount]; + } + } + } + } +} diff --git a/src/core/srcbc/crypto/prng/ThreadedSeedGenerator.cs b/src/core/srcbc/crypto/prng/ThreadedSeedGenerator.cs new file mode 100644 index 0000000..ef5e8dc --- /dev/null +++ b/src/core/srcbc/crypto/prng/ThreadedSeedGenerator.cs @@ -0,0 +1,97 @@ +using System; +using System.Threading; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /** + * A thread based seed generator - one source of randomness. + *

+ * Based on an idea from Marcus Lippert. + *

+ */ + public class ThreadedSeedGenerator + { + private class SeedGenerator + { +#if NETCF_1_0 + // No volatile keyword, but all fields implicitly volatile anyway + private int counter = 0; + private bool stop = false; +#else + private volatile int counter = 0; + private volatile bool stop = false; +#endif + + private void Run(object ignored) + { + while (!this.stop) + { + this.counter++; + } + } + + public byte[] GenerateSeed( + int numBytes, + bool fast) + { + this.counter = 0; + this.stop = false; + + byte[] result = new byte[numBytes]; + int last = 0; + int end = fast ? numBytes : numBytes * 8; + + ThreadPool.QueueUserWorkItem(new WaitCallback(Run)); + + for (int i = 0; i < end; i++) + { + while (this.counter == last) + { + try + { + Thread.Sleep(1); + } + catch (Exception) + { + // ignore + } + } + + last = this.counter; + + if (fast) + { + result[i] = (byte) last; + } + else + { + int bytepos = i / 8; + result[bytepos] = (byte) ((result[bytepos] << 1) | (last & 1)); + } + } + + this.stop = true; + + return result; + } + } + + /** + * Generate seed bytes. Set fast to false for best quality. + *

+ * If fast is set to true, the code should be round about 8 times faster when + * generating a long sequence of random bytes. 20 bytes of random values using + * the fast mode take less than half a second on a Nokia e70. If fast is set to false, + * it takes round about 2500 ms. + *

+ * @param numBytes the number of bytes to generate + * @param fast true if fast mode should be used + */ + public byte[] GenerateSeed( + int numBytes, + bool fast) + { + return new SeedGenerator().GenerateSeed(numBytes, fast); + } + } +} diff --git a/src/core/srcbc/crypto/prng/VMPCRandomGenerator.cs b/src/core/srcbc/crypto/prng/VMPCRandomGenerator.cs new file mode 100644 index 0000000..d3a5a19 --- /dev/null +++ b/src/core/srcbc/crypto/prng/VMPCRandomGenerator.cs @@ -0,0 +1,115 @@ +namespace Org.BouncyCastle.Crypto.Prng +{ + public class VmpcRandomGenerator + : IRandomGenerator + { + private byte n = 0; + + /// + /// Permutation generated by code: + /// + /// // First 1850 fractional digit of Pi number. + /// byte[] key = new BigInteger("14159265358979323846...5068006422512520511").ToByteArray(); + /// s = 0; + /// P = new byte[256]; + /// for (int i = 0; i < 256; i++) + /// { + /// P[i] = (byte) i; + /// } + /// for (int m = 0; m < 768; m++) + /// { + /// s = P[(s + P[m & 0xff] + key[m % key.length]) & 0xff]; + /// byte temp = P[m & 0xff]; + /// P[m & 0xff] = P[s & 0xff]; + /// P[s & 0xff] = temp; + /// } + /// + private byte[] P = + { + (byte) 0xbb, (byte) 0x2c, (byte) 0x62, (byte) 0x7f, (byte) 0xb5, (byte) 0xaa, (byte) 0xd4, + (byte) 0x0d, (byte) 0x81, (byte) 0xfe, (byte) 0xb2, (byte) 0x82, (byte) 0xcb, (byte) 0xa0, (byte) 0xa1, + (byte) 0x08, (byte) 0x18, (byte) 0x71, (byte) 0x56, (byte) 0xe8, (byte) 0x49, (byte) 0x02, (byte) 0x10, + (byte) 0xc4, (byte) 0xde, (byte) 0x35, (byte) 0xa5, (byte) 0xec, (byte) 0x80, (byte) 0x12, (byte) 0xb8, + (byte) 0x69, (byte) 0xda, (byte) 0x2f, (byte) 0x75, (byte) 0xcc, (byte) 0xa2, (byte) 0x09, (byte) 0x36, + (byte) 0x03, (byte) 0x61, (byte) 0x2d, (byte) 0xfd, (byte) 0xe0, (byte) 0xdd, (byte) 0x05, (byte) 0x43, + (byte) 0x90, (byte) 0xad, (byte) 0xc8, (byte) 0xe1, (byte) 0xaf, (byte) 0x57, (byte) 0x9b, (byte) 0x4c, + (byte) 0xd8, (byte) 0x51, (byte) 0xae, (byte) 0x50, (byte) 0x85, (byte) 0x3c, (byte) 0x0a, (byte) 0xe4, + (byte) 0xf3, (byte) 0x9c, (byte) 0x26, (byte) 0x23, (byte) 0x53, (byte) 0xc9, (byte) 0x83, (byte) 0x97, + (byte) 0x46, (byte) 0xb1, (byte) 0x99, (byte) 0x64, (byte) 0x31, (byte) 0x77, (byte) 0xd5, (byte) 0x1d, + (byte) 0xd6, (byte) 0x78, (byte) 0xbd, (byte) 0x5e, (byte) 0xb0, (byte) 0x8a, (byte) 0x22, (byte) 0x38, + (byte) 0xf8, (byte) 0x68, (byte) 0x2b, (byte) 0x2a, (byte) 0xc5, (byte) 0xd3, (byte) 0xf7, (byte) 0xbc, + (byte) 0x6f, (byte) 0xdf, (byte) 0x04, (byte) 0xe5, (byte) 0x95, (byte) 0x3e, (byte) 0x25, (byte) 0x86, + (byte) 0xa6, (byte) 0x0b, (byte) 0x8f, (byte) 0xf1, (byte) 0x24, (byte) 0x0e, (byte) 0xd7, (byte) 0x40, + (byte) 0xb3, (byte) 0xcf, (byte) 0x7e, (byte) 0x06, (byte) 0x15, (byte) 0x9a, (byte) 0x4d, (byte) 0x1c, + (byte) 0xa3, (byte) 0xdb, (byte) 0x32, (byte) 0x92, (byte) 0x58, (byte) 0x11, (byte) 0x27, (byte) 0xf4, + (byte) 0x59, (byte) 0xd0, (byte) 0x4e, (byte) 0x6a, (byte) 0x17, (byte) 0x5b, (byte) 0xac, (byte) 0xff, + (byte) 0x07, (byte) 0xc0, (byte) 0x65, (byte) 0x79, (byte) 0xfc, (byte) 0xc7, (byte) 0xcd, (byte) 0x76, + (byte) 0x42, (byte) 0x5d, (byte) 0xe7, (byte) 0x3a, (byte) 0x34, (byte) 0x7a, (byte) 0x30, (byte) 0x28, + (byte) 0x0f, (byte) 0x73, (byte) 0x01, (byte) 0xf9, (byte) 0xd1, (byte) 0xd2, (byte) 0x19, (byte) 0xe9, + (byte) 0x91, (byte) 0xb9, (byte) 0x5a, (byte) 0xed, (byte) 0x41, (byte) 0x6d, (byte) 0xb4, (byte) 0xc3, + (byte) 0x9e, (byte) 0xbf, (byte) 0x63, (byte) 0xfa, (byte) 0x1f, (byte) 0x33, (byte) 0x60, (byte) 0x47, + (byte) 0x89, (byte) 0xf0, (byte) 0x96, (byte) 0x1a, (byte) 0x5f, (byte) 0x93, (byte) 0x3d, (byte) 0x37, + (byte) 0x4b, (byte) 0xd9, (byte) 0xa8, (byte) 0xc1, (byte) 0x1b, (byte) 0xf6, (byte) 0x39, (byte) 0x8b, + (byte) 0xb7, (byte) 0x0c, (byte) 0x20, (byte) 0xce, (byte) 0x88, (byte) 0x6e, (byte) 0xb6, (byte) 0x74, + (byte) 0x8e, (byte) 0x8d, (byte) 0x16, (byte) 0x29, (byte) 0xf2, (byte) 0x87, (byte) 0xf5, (byte) 0xeb, + (byte) 0x70, (byte) 0xe3, (byte) 0xfb, (byte) 0x55, (byte) 0x9f, (byte) 0xc6, (byte) 0x44, (byte) 0x4a, + (byte) 0x45, (byte) 0x7d, (byte) 0xe2, (byte) 0x6b, (byte) 0x5c, (byte) 0x6c, (byte) 0x66, (byte) 0xa9, + (byte) 0x8c, (byte) 0xee, (byte) 0x84, (byte) 0x13, (byte) 0xa7, (byte) 0x1e, (byte) 0x9d, (byte) 0xdc, + (byte) 0x67, (byte) 0x48, (byte) 0xba, (byte) 0x2e, (byte) 0xe6, (byte) 0xa4, (byte) 0xab, (byte) 0x7c, + (byte) 0x94, (byte) 0x00, (byte) 0x21, (byte) 0xef, (byte) 0xea, (byte) 0xbe, (byte) 0xca, (byte) 0x72, + (byte) 0x4f, (byte) 0x52, (byte) 0x98, (byte) 0x3f, (byte) 0xc2, (byte) 0x14, (byte) 0x7b, (byte) 0x3b, + (byte) 0x54 + }; + + /// Value generated in the same way as P. + private byte s = (byte) 0xbe; + + public VmpcRandomGenerator() + { + } + + public virtual void AddSeedMaterial(byte[] seed) + { + for (int m = 0; m < seed.Length; m++) + { + s = P[(s + P[n & 0xff] + seed[m]) & 0xff]; + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte) ((n + 1) & 0xff); + } + } + + public virtual void AddSeedMaterial(long seed) + { + byte[] s = new byte[4]; + s[3] = (byte) (seed & 0x000000ff); + s[2] = (byte) ((seed & 0x0000ff00) >> 8); + s[1] = (byte) ((seed & 0x00ff0000) >> 16); + s[0] = (byte) ((seed & 0xff000000) >> 24); + AddSeedMaterial(s); + } + + public virtual void NextBytes(byte[] bytes) + { + NextBytes(bytes, 0, bytes.Length); + } + + public virtual void NextBytes(byte[] bytes, int start, int len) + { + lock (P) + { + int end = start + len; + for (int i = start; i != end; i++) + { + s = P[(s + P[n & 0xff]) & 0xff]; + bytes[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + byte temp = P[n & 0xff]; + P[n & 0xff] = P[s & 0xff]; + P[s & 0xff] = temp; + n = (byte) ((n + 1) & 0xff); + } + } + } + } +} diff --git a/src/core/srcbc/crypto/signers/DsaDigestSigner.cs b/src/core/srcbc/crypto/signers/DsaDigestSigner.cs new file mode 100644 index 0000000..a807e34 --- /dev/null +++ b/src/core/srcbc/crypto/signers/DsaDigestSigner.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class DsaDigestSigner + : ISigner + { + private readonly IDigest digest; + private readonly IDsa dsaSigner; + private bool forSigning; + + public DsaDigestSigner( + IDsa signer, + IDigest digest) + { + this.digest = digest; + this.dsaSigner = signer; + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + + AsymmetricKeyParameter k; + + if (parameters is ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.IsPrivate) + { + throw new InvalidKeyException("Signing Requires Private Key."); + } + + if (!forSigning && k.IsPrivate) + { + throw new InvalidKeyException("Verification Requires Public Key."); + } + + Reset(); + + dsaSigner.Init(forSigning, parameters); + } + + /** + * update the internal digest with the byte b + */ + public void Update( + byte input) + { + digest.Update(input); + } + + /** + * update the internal digest with the byte array in + */ + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(input, inOff, length); + } + + /** + * Generate a signature for the message we've been loaded with using + * the key we were initialised with. + */ + public byte[] GenerateSignature() + { + if (!forSigning) + throw new InvalidOperationException("DSADigestSigner not initialised for signature generation."); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + try + { + BigInteger[] sig = dsaSigner.GenerateSignature(hash); + + return DerEncode(sig[0], sig[1]); + } + catch (Exception e) + { + throw new SignatureException(e.Message, e); + } + } + + /// true if the internal state represents the signature described in the passed in array. + public bool VerifySignature( + byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("DSADigestSigner not initialised for verification"); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + BigInteger[] sig; + try + { + sig = DerDecode(signature); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes.", e); + } + + return dsaSigner.VerifySignature(hash, sig[0], sig[1]); + } + + /// Reset the internal state + public void Reset() + { + digest.Reset(); + } + + private byte[] DerEncode( + BigInteger r, + BigInteger s) + { + return new DerSequence(new DerInteger(r), new DerInteger(s)).GetDerEncoded(); + } + + private BigInteger[] DerDecode( + byte[] encoding) + { + Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding); + + return new BigInteger[] + { + ((DerInteger) s[0]).Value, + ((DerInteger) s[1]).Value + }; + } + } +} diff --git a/src/core/srcbc/crypto/signers/DsaSigner.cs b/src/core/srcbc/crypto/signers/DsaSigner.cs new file mode 100644 index 0000000..f6c4f19 --- /dev/null +++ b/src/core/srcbc/crypto/signers/DsaSigner.cs @@ -0,0 +1,136 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * The Digital Signature Algorithm - as described in "Handbook of Applied + * Cryptography", pages 452 - 453. + */ + public class DsaSigner + : IDsa + { + private DsaKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "DSA"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is DsaPrivateKeyParameters)) + throw new InvalidKeyException("DSA private key required for signing"); + + this.key = (DsaPrivateKeyParameters) parameters; + } + else + { + if (!(parameters is DsaPublicKeyParameters)) + throw new InvalidKeyException("DSA public key required for verification"); + + this.key = (DsaPublicKeyParameters) parameters; + } + } + + /** + * Generate a signature for the given message using the key we were + * initialised with. For conventional DSA the message should be a SHA-1 + * hash of the message of interest. + * + * @param message the message that will be verified later. + */ + public BigInteger[] GenerateSignature( + byte[] message) + { + DsaParameters parameters = key.Parameters; + BigInteger q = parameters.Q; + BigInteger m = calculateE(q, message); + BigInteger k; + + do + { + k = new BigInteger(q.BitLength, random); + } + while (k.CompareTo(q) >= 0); + + BigInteger r = parameters.G.ModPow(k, parameters.P).Mod(q); + + k = k.ModInverse(q).Multiply( + m.Add(((DsaPrivateKeyParameters)key).X.Multiply(r))); + + BigInteger s = k.Mod(q); + + return new BigInteger[]{ r, s }; + } + + /** + * return true if the value r and s represent a DSA signature for + * the passed in message for standard DSA the message should be a + * SHA-1 hash of the real message to be verified. + */ + public bool VerifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + DsaParameters parameters = key.Parameters; + BigInteger q = parameters.Q; + BigInteger m = calculateE(q, message); + + if (r.SignValue <= 0 || q.CompareTo(r) <= 0) + { + return false; + } + + if (s.SignValue <= 0 || q.CompareTo(s) <= 0) + { + return false; + } + + BigInteger w = s.ModInverse(q); + + BigInteger u1 = m.Multiply(w).Mod(q); + BigInteger u2 = r.Multiply(w).Mod(q); + + BigInteger p = parameters.P; + u1 = parameters.G.ModPow(u1, p); + u2 = ((DsaPublicKeyParameters)key).Y.ModPow(u2, p); + + BigInteger v = u1.Multiply(u2).Mod(p).Mod(q); + + return v.Equals(r); + } + + private BigInteger calculateE( + BigInteger n, + byte[] message) + { + int length = System.Math.Min(message.Length, n.BitLength / 8); + + return new BigInteger(1, message, 0, length); + } + } +} diff --git a/src/core/srcbc/crypto/signers/ECDsaSigner.cs b/src/core/srcbc/crypto/signers/ECDsaSigner.cs new file mode 100644 index 0000000..1d10143 --- /dev/null +++ b/src/core/srcbc/crypto/signers/ECDsaSigner.cs @@ -0,0 +1,150 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * EC-DSA as described in X9.62 + */ + public class ECDsaSigner + : IDsa + { + private ECKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "ECDSA"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom) parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is ECPrivateKeyParameters)) + throw new InvalidKeyException("EC private key required for signing"); + + this.key = (ECPrivateKeyParameters) parameters; + } + else + { + if (!(parameters is ECPublicKeyParameters)) + throw new InvalidKeyException("EC public key required for verification"); + + this.key = (ECPublicKeyParameters) parameters; + } + } + + // 5.3 pg 28 + /** + * Generate a signature for the given message using the key we were + * initialised with. For conventional DSA the message should be a SHA-1 + * hash of the message of interest. + * + * @param message the message that will be verified later. + */ + public BigInteger[] GenerateSignature( + byte[] message) + { + BigInteger n = key.Parameters.N; + BigInteger e = calculateE(n, message); + + BigInteger r = null; + BigInteger s = null; + + // 5.3.2 + do // Generate s + { + BigInteger k = null; + + do // Generate r + { + do + { + k = new BigInteger(n.BitLength, random); + } + while (k.SignValue == 0); + + ECPoint p = key.Parameters.G.Multiply(k); + + // 5.3.3 + BigInteger x = p.X.ToBigInteger(); + + r = x.Mod(n); + } + while (r.SignValue == 0); + + BigInteger d = ((ECPrivateKeyParameters)key).D; + + s = k.ModInverse(n).Multiply(e.Add(d.Multiply(r))).Mod(n); + } + while (s.SignValue == 0); + + return new BigInteger[]{ r, s }; + } + + // 5.4 pg 29 + /** + * return true if the value r and s represent a DSA signature for + * the passed in message (for standard DSA the message should be + * a SHA-1 hash of the real message to be verified). + */ + public bool VerifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + BigInteger n = key.Parameters.N; + + // r and s should both in the range [1,n-1] + if (r.SignValue < 1 || s.SignValue < 1 + || r.CompareTo(n) >= 0 || s.CompareTo(n) >= 0) + { + return false; + } + + BigInteger e = calculateE(n, message); + BigInteger c = s.ModInverse(n); + + BigInteger u1 = e.Multiply(c).Mod(n); + BigInteger u2 = r.Multiply(c).Mod(n); + + ECPoint G = key.Parameters.G; + ECPoint Q = ((ECPublicKeyParameters) key).Q; + + ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, u1, Q, u2); + + BigInteger v = point.X.ToBigInteger().Mod(n); + + return v.Equals(r); + } + + private BigInteger calculateE( + BigInteger n, + byte[] message) + { + int length = System.Math.Min(message.Length, n.BitLength / 8); + + return new BigInteger(1, message, 0, length); + } + } +} diff --git a/src/core/srcbc/crypto/signers/ECGOST3410Signer.cs b/src/core/srcbc/crypto/signers/ECGOST3410Signer.cs new file mode 100644 index 0000000..aef64a4 --- /dev/null +++ b/src/core/srcbc/crypto/signers/ECGOST3410Signer.cs @@ -0,0 +1,154 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * GOST R 34.10-2001 Signature Algorithm + */ + public class ECGost3410Signer + : IDsa + { + private ECKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "ECGOST3410"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is ECPrivateKeyParameters)) + throw new InvalidKeyException("EC private key required for signing"); + + this.key = (ECPrivateKeyParameters) parameters; + } + else + { + if (!(parameters is ECPublicKeyParameters)) + throw new InvalidKeyException("EC public key required for verification"); + + this.key = (ECPublicKeyParameters)parameters; + } + } + + /** + * generate a signature for the given message using the key we were + * initialised with. For conventional GOST3410 the message should be a GOST3411 + * hash of the message of interest. + * + * @param message the message that will be verified later. + */ + public BigInteger[] GenerateSignature( + byte[] message) + { + byte[] mRev = new byte[message.Length]; // conversion is little-endian + for (int i = 0; i != mRev.Length; i++) + { + mRev[i] = message[mRev.Length - 1 - i]; + } + + BigInteger e = new BigInteger(1, mRev); + BigInteger n = key.Parameters.N; + + BigInteger r = null; + BigInteger s = null; + + do // generate s + { + BigInteger k = null; + + do // generate r + { + do + { + k = new BigInteger(n.BitLength, random); + } + while (k.SignValue == 0); + + ECPoint p = key.Parameters.G.Multiply(k); + + BigInteger x = p.X.ToBigInteger(); + + r = x.Mod(n); + } + while (r.SignValue == 0); + + BigInteger d = ((ECPrivateKeyParameters)key).D; + + s = (k.Multiply(e)).Add(d.Multiply(r)).Mod(n); + } + while (s.SignValue == 0); + + return new BigInteger[]{ r, s }; + } + + /** + * return true if the value r and s represent a GOST3410 signature for + * the passed in message (for standard GOST3410 the message should be + * a GOST3411 hash of the real message to be verified). + */ + public bool VerifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + byte[] mRev = new byte[message.Length]; // conversion is little-endian + for (int i = 0; i != mRev.Length; i++) + { + mRev[i] = message[mRev.Length - 1 - i]; + } + + BigInteger e = new BigInteger(1, mRev); + BigInteger n = key.Parameters.N; + + // r in the range [1,n-1] + if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0) + { + return false; + } + + // s in the range [1,n-1] + if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0) + { + return false; + } + + BigInteger v = e.ModInverse(n); + + BigInteger z1 = s.Multiply(v).Mod(n); + BigInteger z2 = (n.Subtract(r)).Multiply(v).Mod(n); + + ECPoint G = key.Parameters.G; // P + ECPoint Q = ((ECPublicKeyParameters)key).Q; + + ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, z1, Q, z2); + + BigInteger R = point.X.ToBigInteger().Mod(n); + + return R.Equals(r); + } + } +} diff --git a/src/core/srcbc/crypto/signers/ECNRSigner.cs b/src/core/srcbc/crypto/signers/ECNRSigner.cs new file mode 100644 index 0000000..4c17e7c --- /dev/null +++ b/src/core/srcbc/crypto/signers/ECNRSigner.cs @@ -0,0 +1,186 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * EC-NR as described in IEEE 1363-2000 + */ + public class ECNRSigner + : IDsa + { + private bool forSigning; + private ECKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "ECNR"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom) parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is ECPrivateKeyParameters)) + throw new InvalidKeyException("EC private key required for signing"); + + this.key = (ECPrivateKeyParameters) parameters; + } + else + { + if (!(parameters is ECPublicKeyParameters)) + throw new InvalidKeyException("EC public key required for verification"); + + this.key = (ECPublicKeyParameters) parameters; + } + } + + // Section 7.2.5 ECSP-NR, pg 34 + /** + * generate a signature for the given message using the key we were + * initialised with. Generally, the order of the curve should be at + * least as long as the hash of the message of interest, and with + * ECNR it *must* be at least as long. + * + * @param digest the digest to be signed. + * @exception DataLengthException if the digest is longer than the key allows + */ + public BigInteger[] GenerateSignature( + byte[] message) + { + if (!this.forSigning) + { + // not properly initilaized... deal with it + throw new InvalidOperationException("not initialised for signing"); + } + + BigInteger n = ((ECPrivateKeyParameters) this.key).Parameters.N; + int nBitLength = n.BitLength; + + BigInteger e = new BigInteger(1, message); + int eBitLength = e.BitLength; + + ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)key; + + if (eBitLength > nBitLength) + { + throw new DataLengthException("input too large for ECNR key."); + } + + BigInteger r = null; + BigInteger s = null; + + AsymmetricCipherKeyPair tempPair; + do // generate r + { + // generate another, but very temporary, key pair using + // the same EC parameters + ECKeyPairGenerator keyGen = new ECKeyPairGenerator(); + + keyGen.Init(new ECKeyGenerationParameters(privKey.Parameters, this.random)); + + tempPair = keyGen.GenerateKeyPair(); + + // BigInteger Vx = tempPair.getPublic().getW().getAffineX(); + ECPublicKeyParameters V = (ECPublicKeyParameters) tempPair.Public; // get temp's public key + BigInteger Vx = V.Q.X.ToBigInteger(); // get the point's x coordinate + + r = Vx.Add(e).Mod(n); + } + while (r.SignValue == 0); + + // generate s + BigInteger x = privKey.D; // private key value + BigInteger u = ((ECPrivateKeyParameters) tempPair.Private).D; // temp's private key value + s = u.Subtract(r.Multiply(x)).Mod(n); + + return new BigInteger[]{ r, s }; + } + + // Section 7.2.6 ECVP-NR, pg 35 + /** + * return true if the value r and s represent a signature for the + * message passed in. Generally, the order of the curve should be at + * least as long as the hash of the message of interest, and with + * ECNR, it *must* be at least as long. But just in case the signer + * applied mod(n) to the longer digest, this implementation will + * apply mod(n) during verification. + * + * @param digest the digest to be verified. + * @param r the r value of the signature. + * @param s the s value of the signature. + * @exception DataLengthException if the digest is longer than the key allows + */ + public bool VerifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + if (this.forSigning) + { + // not properly initilaized... deal with it + throw new InvalidOperationException("not initialised for verifying"); + } + + ECPublicKeyParameters pubKey = (ECPublicKeyParameters)key; + BigInteger n = pubKey.Parameters.N; + int nBitLength = n.BitLength; + + BigInteger e = new BigInteger(1, message); + int eBitLength = e.BitLength; + + if (eBitLength > nBitLength) + { + throw new DataLengthException("input too large for ECNR key."); + } + + // r in the range [1,n-1] + if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0) + { + return false; + } + + // TODO So why is this different from the spec? + // s in the range [0,n-1] NB: ECNR spec says 0 + if (s.CompareTo(BigInteger.Zero) < 0 || s.CompareTo(n) >= 0) + { + return false; + } + + // compute P = sG + rW + + ECPoint G = pubKey.Parameters.G; + ECPoint W = pubKey.Q; + // calculate P using Bouncy math + ECPoint P = ECAlgorithms.SumOfTwoMultiplies(G, s, W, r); + + BigInteger x = P.X.ToBigInteger(); + BigInteger t = r.Subtract(x).Mod(n); + + return t.Equals(e); + } + } +} diff --git a/src/core/srcbc/crypto/signers/GOST3410DigestSigner.cs b/src/core/srcbc/crypto/signers/GOST3410DigestSigner.cs new file mode 100644 index 0000000..fd990fc --- /dev/null +++ b/src/core/srcbc/crypto/signers/GOST3410DigestSigner.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class Gost3410DigestSigner + : ISigner + { + private readonly IDigest digest; + private readonly IDsa dsaSigner; + private bool forSigning; + + public Gost3410DigestSigner( + IDsa signer, + IDigest digest) + { + this.dsaSigner = signer; + this.digest = digest; + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + + AsymmetricKeyParameter k; + if (parameters is ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.IsPrivate) + { + throw new InvalidKeyException("Signing Requires Private Key."); + } + + if (!forSigning && k.IsPrivate) + { + throw new InvalidKeyException("Verification Requires Public Key."); + } + + Reset(); + + dsaSigner.Init(forSigning, parameters); + } + + /** + * update the internal digest with the byte b + */ + public void Update( + byte input) + { + digest.Update(input); + } + + /** + * update the internal digest with the byte array in + */ + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(input, inOff, length); + } + + /** + * Generate a signature for the message we've been loaded with using + * the key we were initialised with. + */ + public byte[] GenerateSignature() + { + if (!forSigning) + throw new InvalidOperationException("GOST3410DigestSigner not initialised for signature generation."); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + try + { + BigInteger[] sig = dsaSigner.GenerateSignature(hash); + byte[] sigBytes = new byte[64]; + + // TODO Add methods to allow writing BigInteger to existing byte array? + byte[] r = sig[0].ToByteArrayUnsigned(); + byte[] s = sig[1].ToByteArrayUnsigned(); + s.CopyTo(sigBytes, 32 - s.Length); + r.CopyTo(sigBytes, 64 - r.Length); + return sigBytes; + } + catch (Exception e) + { + throw new SignatureException(e.Message, e); + } + } + + /// true if the internal state represents the signature described in the passed in array. + public bool VerifySignature( + byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("DSADigestSigner not initialised for verification"); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + BigInteger R, S; + try + { + R = new BigInteger(1, signature, 32, 32); + S = new BigInteger(1, signature, 0, 32); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes.", e); + } + + return dsaSigner.VerifySignature(hash, R, S); + } + + /// Reset the internal state + public void Reset() + { + digest.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/signers/GOST3410Signer.cs b/src/core/srcbc/crypto/signers/GOST3410Signer.cs new file mode 100644 index 0000000..25df7a6 --- /dev/null +++ b/src/core/srcbc/crypto/signers/GOST3410Signer.cs @@ -0,0 +1,132 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * Gost R 34.10-94 Signature Algorithm + */ + public class Gost3410Signer + : IDsa + { + private Gost3410KeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "GOST3410"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is Gost3410PrivateKeyParameters)) + throw new InvalidKeyException("GOST3410 private key required for signing"); + + this.key = (Gost3410PrivateKeyParameters) parameters; + } + else + { + if (!(parameters is Gost3410PublicKeyParameters)) + throw new InvalidKeyException("GOST3410 public key required for signing"); + + this.key = (Gost3410PublicKeyParameters) parameters; + } + } + + /** + * generate a signature for the given message using the key we were + * initialised with. For conventional Gost3410 the message should be a Gost3411 + * hash of the message of interest. + * + * @param message the message that will be verified later. + */ + public BigInteger[] GenerateSignature( + byte[] message) + { + byte[] mRev = new byte[message.Length]; // conversion is little-endian + for (int i = 0; i != mRev.Length; i++) + { + mRev[i] = message[mRev.Length - 1 - i]; + } + + BigInteger m = new BigInteger(1, mRev); + Gost3410Parameters parameters = key.Parameters; + BigInteger k; + + do + { + k = new BigInteger(parameters.Q.BitLength, random); + } + while (k.CompareTo(parameters.Q) >= 0); + + BigInteger r = parameters.A.ModPow(k, parameters.P).Mod(parameters.Q); + + BigInteger s = k.Multiply(m). + Add(((Gost3410PrivateKeyParameters)key).X.Multiply(r)). + Mod(parameters.Q); + + return new BigInteger[]{ r, s }; + } + + /** + * return true if the value r and s represent a Gost3410 signature for + * the passed in message for standard Gost3410 the message should be a + * Gost3411 hash of the real message to be verified. + */ + public bool VerifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + byte[] mRev = new byte[message.Length]; // conversion is little-endian + for (int i = 0; i != mRev.Length; i++) + { + mRev[i] = message[mRev.Length - 1 - i]; + } + + BigInteger m = new BigInteger(1, mRev); + Gost3410Parameters parameters = key.Parameters; + + if (r.SignValue < 0 || parameters.Q.CompareTo(r) <= 0) + { + return false; + } + + if (s.SignValue < 0 || parameters.Q.CompareTo(s) <= 0) + { + return false; + } + + BigInteger v = m.ModPow(parameters.Q.Subtract(BigInteger.Two), parameters.Q); + + BigInteger z1 = s.Multiply(v).Mod(parameters.Q); + BigInteger z2 = (parameters.Q.Subtract(r)).Multiply(v).Mod(parameters.Q); + + z1 = parameters.A.ModPow(z1, parameters.P); + z2 = ((Gost3410PublicKeyParameters)key).Y.ModPow(z2, parameters.P); + + BigInteger u = z1.Multiply(z2).Mod(parameters.P).Mod(parameters.Q); + + return u.Equals(r); + } + } +} diff --git a/src/core/srcbc/crypto/signers/Iso9796d2PssSigner.cs b/src/core/srcbc/crypto/signers/Iso9796d2PssSigner.cs new file mode 100644 index 0000000..a91c599 --- /dev/null +++ b/src/core/srcbc/crypto/signers/Iso9796d2PssSigner.cs @@ -0,0 +1,561 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /// ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3). + ///

+ /// Note: the usual length for the salt is the length of the hash + /// function used in bytes.

+ ///
+ public class Iso9796d2PssSigner + : ISignerWithRecovery + { + /// + /// Return a reference to the recoveredMessage message. + /// + /// The full/partial recoveredMessage message. + /// + public byte[] GetRecoveredMessage() + { + return recoveredMessage; + } + + public const int TrailerImplicit = 0xBC; + public const int TrailerRipeMD160 = 0x31CC; + public const int TrailerRipeMD128 = 0x32CC; + public const int TrailerSha1 = 0x33CC; + + private IDigest digest; + private IAsymmetricBlockCipher cipher; + + private SecureRandom random; + private byte[] standardSalt; + + private int hLen; + private int trailer; + private int keyBits; + private byte[] block; + private byte[] mBuf; + private int messageLength; + private readonly int saltLength; + private bool fullMessage; + private byte[] recoveredMessage; + + /// + /// Generate a signer for the with either implicit or explicit trailers + /// for ISO9796-2, scheme 2 or 3. + /// + /// base cipher to use for signature creation/verification + /// digest to use. + /// length of salt in bytes. + /// whether or not the trailer is implicit or gives the hash. + public Iso9796d2PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest, + int saltLength, + bool isImplicit) + { + this.cipher = cipher; + this.digest = digest; + this.hLen = digest.GetDigestSize(); + this.saltLength = saltLength; + + if (isImplicit) + { + trailer = TrailerImplicit; + } + else + { + if (digest is Sha1Digest) + { + trailer = TrailerSha1; + } + else if (digest is RipeMD160Digest) + { + trailer = TrailerRipeMD160; + } + else if (digest is RipeMD128Digest) + { + trailer = TrailerRipeMD128; + } + else + { + throw new ArgumentException("no valid trailer for digest"); + } + } + } + + /// Constructor for a signer with an explicit digest trailer. + /// + /// + /// cipher to use. + /// + /// digest to sign with. + /// + /// length of salt in bytes. + /// + public Iso9796d2PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest, + int saltLength) + : this(cipher, digest, saltLength, false) + { + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; } + } + + /// Initialise the signer. + /// true if for signing, false if for verification. + /// parameters for signature generation/verification. If the + /// parameters are for generation they should be a ParametersWithRandom, + /// a ParametersWithSalt, or just an RsaKeyParameters object. If RsaKeyParameters + /// are passed in a SecureRandom will be created. + /// + /// if wrong parameter type or a fixed + /// salt is passed in which is the wrong length. + /// + public virtual void Init( + bool forSigning, + ICipherParameters parameters) + { + RsaKeyParameters kParam; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom) parameters; + + kParam = (RsaKeyParameters) p.Parameters; + + if (forSigning) + { + random = p.Random; + } + } + else if (parameters is ParametersWithSalt) + { + if (!forSigning) + throw new ArgumentException("ParametersWithSalt only valid for signing", "parameters"); + + ParametersWithSalt p = (ParametersWithSalt) parameters; + + kParam = (RsaKeyParameters) p.Parameters; + standardSalt = p.GetSalt(); + + if (standardSalt.Length != saltLength) + throw new ArgumentException("Fixed salt is of wrong length"); + } + else + { + kParam = (RsaKeyParameters) parameters; + + if (forSigning) + { + random = new SecureRandom(); + } + } + + cipher.Init(forSigning, kParam); + + keyBits = kParam.Modulus.BitLength; + + block = new byte[(keyBits + 7) / 8]; + + if (trailer == TrailerImplicit) + { + mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 1]; + } + else + { + mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 2]; + } + + Reset(); + } + + /// compare two byte arrays. + private bool IsSameAs(byte[] a, byte[] b) + { + if (messageLength != b.Length) + { + return false; + } + + for (int i = 0; i != b.Length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + /// clear possible sensitive data + private void ClearBlock( + byte[] block) + { + Array.Clear(block, 0, block.Length); + } + + /// update the internal digest with the byte b + public virtual void Update( + byte input) + { + if (messageLength < mBuf.Length) + { + mBuf[messageLength++] = input; + } + else + { + digest.Update(input); + } + } + + /// update the internal digest with the byte array in + public virtual void BlockUpdate( + byte[] input, + int inOff, + int length) + { + while (length > 0 && messageLength < mBuf.Length) + { + this.Update(input[inOff]); + inOff++; + length--; + } + + if (length > 0) + { + digest.BlockUpdate(input, inOff, length); + } + } + + /// reset the internal state + public virtual void Reset() + { + digest.Reset(); + messageLength = 0; + if (mBuf != null) + { + ClearBlock(mBuf); + } + if (recoveredMessage != null) + { + ClearBlock(recoveredMessage); + recoveredMessage = null; + } + fullMessage = false; + } + + /// Generate a signature for the loaded message using the key we were + /// initialised with. + /// + public byte[] GenerateSignature() + { + int digSize = digest.GetDigestSize(); + byte[] m2Hash = new byte[digSize]; + digest.DoFinal(m2Hash, 0); + + byte[] C = new byte[8]; + LtoOSP(messageLength * 8, C); + + digest.BlockUpdate(C, 0, C.Length); + digest.BlockUpdate(mBuf, 0, messageLength); + digest.BlockUpdate(m2Hash, 0, m2Hash.Length); + + byte[] salt; + if (standardSalt != null) + { + salt = standardSalt; + } + else + { + salt = new byte[saltLength]; + random.NextBytes(salt); + } + + digest.BlockUpdate(salt, 0, salt.Length); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + int tLength = 2; + if (trailer == TrailerImplicit) + { + tLength = 1; + } + + int off = block.Length - messageLength - salt.Length - hLen - tLength - 1; + + block[off] = (byte) (0x01); + + Array.Copy(mBuf, 0, block, off + 1, messageLength); + Array.Copy(salt, 0, block, off + 1 + messageLength, salt.Length); + + byte[] dbMask = MaskGeneratorFunction1(hash, 0, hash.Length, block.Length - hLen - tLength); + for (int i = 0; i != dbMask.Length; i++) + { + block[i] ^= dbMask[i]; + } + + Array.Copy(hash, 0, block, block.Length - hLen - tLength, hLen); + + if (trailer == TrailerImplicit) + { + block[block.Length - 1] = (byte)TrailerImplicit; + } + else + { + block[block.Length - 2] = (byte) ((uint)trailer >> 8); + block[block.Length - 1] = (byte) trailer; + } + + block[0] &= (byte) (0x7f); + + byte[] b = cipher.ProcessBlock(block, 0, block.Length); + + ClearBlock(mBuf); + ClearBlock(block); + messageLength = 0; + + return b; + } + + /// return true if the signature represents a ISO9796-2 signature + /// for the passed in message. + /// + public virtual bool VerifySignature( + byte[] signature) + { + byte[] block = cipher.ProcessBlock(signature, 0, signature.Length); + + // + // adjust block size for leading zeroes if necessary + // + int expectedSize = (keyBits + 7) / 8; + if (block.Length < expectedSize) + { + byte[] tmp = new byte[expectedSize]; + block.CopyTo(tmp, tmp.Length - block.Length); + ClearBlock(block); + block = tmp; + } + + int tLength; + + if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0) + { + tLength = 1; + } + else + { + int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF); + + switch (sigTrail) + { + case TrailerRipeMD160: + if (!(digest is RipeMD160Digest)) + { + throw new ArgumentException("signer should be initialised with RipeMD160"); + } + break; + case TrailerSha1: + if (!(digest is Sha1Digest)) + { + throw new ArgumentException("signer should be initialised with SHA1"); + } + break; + case TrailerRipeMD128: + if (!(digest is RipeMD128Digest)) + { + throw new ArgumentException("signer should be initialised with RipeMD128"); + } + break; + default: + throw new ArgumentException("unrecognised hash in signature"); + } + + tLength = 2; + } + + // + // calculate H(m2) + // + byte[] m2Hash = new byte[hLen]; + digest.DoFinal(m2Hash, 0); + + // + // remove the mask + // + byte[] dbMask = MaskGeneratorFunction1(block, block.Length - hLen - tLength, hLen, block.Length - hLen - tLength); + for (int i = 0; i != dbMask.Length; i++) + { + block[i] ^= dbMask[i]; + } + + block[0] &= 0x7f; + + // + // find out how much padding we've got + // + int mStart = 0; + while (mStart < block.Length) + { + if (block[mStart++] == 0x01) + break; + } + + if (mStart >= block.Length) + { + ClearBlock(block); + return false; + } + + fullMessage = (mStart > 1); + + // TODO Should we check if a standardSalt was set and, if so, use its length instead? + recoveredMessage = new byte[dbMask.Length - mStart - saltLength]; + + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + + // + // check the hashes + // + byte[] C = new byte[8]; + LtoOSP(recoveredMessage.Length * 8, C); + + digest.BlockUpdate(C, 0, C.Length); + + if (recoveredMessage.Length != 0) + { + digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length); + } + + digest.BlockUpdate(m2Hash, 0, m2Hash.Length); + + // Update for the salt + digest.BlockUpdate(block, mStart + recoveredMessage.Length, saltLength); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + int off = block.Length - tLength - hash.Length; + + for (int i = 0; i != hash.Length; i++) + { + if (hash[i] != block[off + i]) + { + ClearBlock(block); + ClearBlock(hash); + ClearBlock(recoveredMessage); + fullMessage = false; + + return false; + } + } + + ClearBlock(block); + ClearBlock(hash); + + // + // if they've input a message check what we've recovered against + // what was input. + // + if (messageLength != 0) + { + if (!IsSameAs(mBuf, recoveredMessage)) + { + ClearBlock(mBuf); + return false; + } + + messageLength = 0; + } + + ClearBlock(mBuf); + return true; + } + + /// + /// Return true if the full message was recoveredMessage. + /// + /// true on full message recovery, false otherwise, or if not sure. + /// + public virtual bool HasFullMessage() + { + return fullMessage; + } + + /// int to octet string. + /// int to octet string. + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint)i >> 24); + sp[1] = (byte)((uint)i >> 16); + sp[2] = (byte)((uint)i >> 8); + sp[3] = (byte)((uint)i >> 0); + } + + /// long to octet string. + private void LtoOSP(long l, byte[] sp) + { + sp[0] = (byte)((ulong)l >> 56); + sp[1] = (byte)((ulong)l >> 48); + sp[2] = (byte)((ulong)l >> 40); + sp[3] = (byte)((ulong)l >> 32); + sp[4] = (byte)((ulong)l >> 24); + sp[5] = (byte)((ulong)l >> 16); + sp[6] = (byte)((ulong)l >> 8); + sp[7] = (byte)((ulong)l >> 0); + } + + /// mask generator function, as described in Pkcs1v2. + private byte[] MaskGeneratorFunction1( + byte[] Z, + int zOff, + int zLen, + int length) + { + byte[] mask = new byte[length]; + byte[] hashBuf = new byte[hLen]; + byte[] C = new byte[4]; + int counter = 0; + + digest.Reset(); + + do + { + ItoOSP(counter, C); + + digest.BlockUpdate(Z, zOff, zLen); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hLen, hLen); + } + while (++counter < (length / hLen)); + + if ((counter * hLen) < length) + { + ItoOSP(counter, C); + + digest.BlockUpdate(Z, zOff, zLen); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hLen, mask.Length - (counter * hLen)); + } + + return mask; + } + } +} diff --git a/src/core/srcbc/crypto/signers/Iso9796d2Signer.cs b/src/core/srcbc/crypto/signers/Iso9796d2Signer.cs new file mode 100644 index 0000000..ef924f6 --- /dev/null +++ b/src/core/srcbc/crypto/signers/Iso9796d2Signer.cs @@ -0,0 +1,451 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Signers +{ + + /// ISO9796-2 - mechanism using a hash function with recovery (scheme 1) + public class Iso9796d2Signer : ISignerWithRecovery + { + /// + /// Return a reference to the recoveredMessage message. + /// + /// The full/partial recoveredMessage message. + /// + public byte[] GetRecoveredMessage() + { + return recoveredMessage; + } + + public const int TrailerImplicit = 0xBC; + public const int TrailerRipeMD160 = 0x31CC; + public const int TrailerRipeMD128 = 0x32CC; + public const int TrailerSha1 = 0x33CC; + + private IDigest digest; + private IAsymmetricBlockCipher cipher; + + private int trailer; + private int keyBits; + private byte[] block; + private byte[] mBuf; + private int messageLength; + private bool fullMessage; + private byte[] recoveredMessage; + + /// + /// Generate a signer for the with either implicit or explicit trailers + /// for ISO9796-2. + /// + /// base cipher to use for signature creation/verification + /// digest to use. + /// whether or not the trailer is implicit or gives the hash. + public Iso9796d2Signer( + IAsymmetricBlockCipher cipher, + IDigest digest, + bool isImplicit) + { + this.cipher = cipher; + this.digest = digest; + + if (isImplicit) + { + trailer = TrailerImplicit; + } + else + { + if (digest is Sha1Digest) + { + trailer = TrailerSha1; + } + else if (digest is RipeMD160Digest) + { + trailer = TrailerRipeMD160; + } + else if (digest is RipeMD128Digest) + { + trailer = TrailerRipeMD128; + } + else + { + throw new System.ArgumentException("no valid trailer for digest"); + } + } + } + + /// Constructor for a signer with an explicit digest trailer. + /// + /// + /// cipher to use. + /// + /// digest to sign with. + /// + public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest):this(cipher, digest, false) + { + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + RsaKeyParameters kParam = (RsaKeyParameters) parameters; + + cipher.Init(forSigning, kParam); + + keyBits = kParam.Modulus.BitLength; + + block = new byte[(keyBits + 7) / 8]; + if (trailer == TrailerImplicit) + { + mBuf = new byte[block.Length - digest.GetDigestSize() - 2]; + } + else + { + mBuf = new byte[block.Length - digest.GetDigestSize() - 3]; + } + + Reset(); + } + + /// compare two byte arrays. + private bool IsSameAs(byte[] a, byte[] b) + { + if (messageLength > mBuf.Length) + { + if (mBuf.Length > b.Length) + { + return false; + } + + for (int i = 0; i != mBuf.Length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + } + else + { + if (messageLength != b.Length) + { + return false; + } + + for (int i = 0; i != b.Length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + } + + return true; + } + + /// clear possible sensitive data + private void ClearBlock( + byte[] block) + { + Array.Clear(block, 0, block.Length); + } + + /// update the internal digest with the byte b + public void Update( + byte input) + { + digest.Update(input); + + if (messageLength < mBuf.Length) + { + mBuf[messageLength] = input; + } + + messageLength++; + } + + /// update the internal digest with the byte array in + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(input, inOff, length); + + if (messageLength < mBuf.Length) + { + for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++) + { + mBuf[messageLength + i] = input[inOff + i]; + } + } + + messageLength += length; + } + + /// reset the internal state + public virtual void Reset() + { + digest.Reset(); + messageLength = 0; + ClearBlock(mBuf); + + if (recoveredMessage != null) + { + ClearBlock(recoveredMessage); + } + + recoveredMessage = null; + fullMessage = false; + } + + /// Generate a signature for the loaded message using the key we were + /// initialised with. + /// + public virtual byte[] GenerateSignature() + { + int digSize = digest.GetDigestSize(); + + int t = 0; + int delta = 0; + + if (trailer == TrailerImplicit) + { + t = 8; + delta = block.Length - digSize - 1; + digest.DoFinal(block, delta); + block[block.Length - 1] = (byte) TrailerImplicit; + } + else + { + t = 16; + delta = block.Length - digSize - 2; + digest.DoFinal(block, delta); + block[block.Length - 2] = (byte) ((uint)trailer >> 8); + block[block.Length - 1] = (byte) trailer; + } + + byte header = 0; + int x = (digSize + messageLength) * 8 + t + 4 - keyBits; + + if (x > 0) + { + int mR = messageLength - ((x + 7) / 8); + header = (byte) (0x60); + + delta -= mR; + + Array.Copy(mBuf, 0, block, delta, mR); + } + else + { + header = (byte) (0x40); + delta -= messageLength; + + Array.Copy(mBuf, 0, block, delta, messageLength); + } + + if ((delta - 1) > 0) + { + for (int i = delta - 1; i != 0; i--) + { + block[i] = (byte) 0xbb; + } + block[delta - 1] ^= (byte) 0x01; + block[0] = (byte) 0x0b; + block[0] |= header; + } + else + { + block[0] = (byte) 0x0a; + block[0] |= header; + } + + byte[] b = cipher.ProcessBlock(block, 0, block.Length); + + ClearBlock(mBuf); + ClearBlock(block); + + return b; + } + + /// return true if the signature represents a ISO9796-2 signature + /// for the passed in message. + /// + public virtual bool VerifySignature(byte[] signature) + { + byte[] block = cipher.ProcessBlock(signature, 0, signature.Length); + + if (((block[0] & 0xC0) ^ 0x40) != 0) + { + ClearBlock(mBuf); + ClearBlock(block); + + return false; + } + + if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0) + { + ClearBlock(mBuf); + ClearBlock(block); + + return false; + } + + int delta = 0; + + if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0) + { + delta = 1; + } + else + { + int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF); + + switch (sigTrail) + { + case TrailerRipeMD160: + if (!(digest is RipeMD160Digest)) + { + throw new ArgumentException("signer should be initialised with RipeMD160"); + } + break; + case TrailerSha1: + if (!(digest is Sha1Digest)) + { + throw new ArgumentException("signer should be initialised with SHA1"); + } + break; + case TrailerRipeMD128: + if (!(digest is RipeMD128Digest)) + { + throw new ArgumentException("signer should be initialised with RipeMD128"); + } + break; + default: + throw new ArgumentException("unrecognised hash in signature"); + } + + delta = 2; + } + + // + // find out how much padding we've got + // + int mStart = 0; + for (; mStart != block.Length; mStart++) + { + if (((block[mStart] & 0x0f) ^ 0x0a) == 0) + { + break; + } + } + + mStart++; + + // + // check the hashes + // + byte[] hash = new byte[digest.GetDigestSize()]; + + int off = block.Length - delta - hash.Length; + + // + // there must be at least one byte of message string + // + if ((off - mStart) <= 0) + { + ClearBlock(mBuf); + ClearBlock(block); + + return false; + } + + // + // if we contain the whole message as well, check the hash of that. + // + if ((block[0] & 0x20) == 0) + { + fullMessage = true; + + digest.Reset(); + digest.BlockUpdate(block, mStart, off - mStart); + digest.DoFinal(hash, 0); + + for (int i = 0; i != hash.Length; i++) + { + block[off + i] ^= hash[i]; + if (block[off + i] != 0) + { + ClearBlock(mBuf); + ClearBlock(block); + + return false; + } + } + + recoveredMessage = new byte[off - mStart]; + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + } + else + { + fullMessage = false; + + digest.DoFinal(hash, 0); + + for (int i = 0; i != hash.Length; i++) + { + block[off + i] ^= hash[i]; + if (block[off + i] != 0) + { + ClearBlock(mBuf); + ClearBlock(block); + + return false; + } + } + + recoveredMessage = new byte[off - mStart]; + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + } + + // + // if they've input a message check what we've recovered against + // what was input. + // + if (messageLength != 0) + { + if (!IsSameAs(mBuf, recoveredMessage)) + { + ClearBlock(mBuf); + ClearBlock(block); + ClearBlock(recoveredMessage); + + return false; + } + } + + ClearBlock(mBuf); + ClearBlock(block); + + return true; + } + + /// + /// Return true if the full message was recoveredMessage. + /// + /// true on full message recovery, false otherwise. + /// + public virtual bool HasFullMessage() + { + return fullMessage; + } + } +} diff --git a/src/core/srcbc/crypto/signers/PssSigner.cs b/src/core/srcbc/crypto/signers/PssSigner.cs new file mode 100644 index 0000000..063d68f --- /dev/null +++ b/src/core/srcbc/crypto/signers/PssSigner.cs @@ -0,0 +1,299 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /// RSA-PSS as described in Pkcs# 1 v 2.1. + ///

+ /// Note: the usual value for the salt length is the number of + /// bytes in the hash function.

+ ///
+ public class PssSigner + : ISigner + { + public const byte TrailerImplicit = (byte)0xBC; + + private readonly IDigest digest; + private readonly IAsymmetricBlockCipher cipher; + + private SecureRandom random; + + private int hLen; + private int sLen; + private int emBits; + private byte[] salt; + private byte[] mDash; + private byte[] block; + private byte trailer; + + public PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest) + : this(cipher, digest, digest.GetDigestSize()) + { + } + + /// Basic constructor + /// the asymmetric cipher to use. + /// the digest to use. + /// the length of the salt to use (in bytes). + public PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest, + int saltLen) + : this(cipher, digest, saltLen, TrailerImplicit) + { + } + + public PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest, + int saltLen, + byte trailer) + { + this.cipher = cipher; + this.digest = digest; + this.hLen = digest.GetDigestSize(); + this.sLen = saltLen; + this.salt = new byte[saltLen]; + this.mDash = new byte[8 + saltLen + hLen]; + this.trailer = trailer; + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "withRSAandMGF1"; } + } + + public virtual void Init( + bool forSigning, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom) parameters; + + parameters = p.Parameters; + random = p.Random; + } + else + { + if (forSigning) + { + random = new SecureRandom(); + } + } + + cipher.Init(forSigning, parameters); + + RsaKeyParameters kParam; + if (parameters is RsaBlindingParameters) + { + kParam = ((RsaBlindingParameters) parameters).PublicKey; + } + else + { + kParam = (RsaKeyParameters) parameters; + } + + emBits = kParam.Modulus.BitLength - 1; + + block = new byte[(emBits + 7) / 8]; + } + + /// clear possible sensitive data + private void ClearBlock( + byte[] block) + { + Array.Clear(block, 0, block.Length); + } + + /// update the internal digest with the byte b + public virtual void Update( + byte input) + { + digest.Update(input); + } + + /// update the internal digest with the byte array in + public virtual void BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(input, inOff, length); + } + + /// reset the internal state + public virtual void Reset() + { + digest.Reset(); + } + + /// Generate a signature for the message we've been loaded with using + /// the key we were initialised with. + /// + public virtual byte[] GenerateSignature() + { + if (emBits < (8 * hLen + 8 * sLen + 9)) + { + throw new DataLengthException("encoding error"); + } + + digest.DoFinal(mDash, mDash.Length - hLen - sLen); + + if (sLen != 0) + { + random.NextBytes(salt); + salt.CopyTo(mDash, mDash.Length - sLen); + } + + byte[] h = new byte[hLen]; + + digest.BlockUpdate(mDash, 0, mDash.Length); + + digest.DoFinal(h, 0); + + block[block.Length - sLen - 1 - hLen - 1] = (byte) (0x01); + salt.CopyTo(block, block.Length - sLen - hLen - 1); + + byte[] dbMask = MaskGeneratorFunction1(h, 0, h.Length, block.Length - hLen - 1); + for (int i = 0; i != dbMask.Length; i++) + { + block[i] ^= dbMask[i]; + } + + block[0] &= (byte) ((0xff >> ((block.Length * 8) - emBits))); + + h.CopyTo(block, block.Length - hLen - 1); + + block[block.Length - 1] = trailer; + + byte[] b = cipher.ProcessBlock(block, 0, block.Length); + + ClearBlock(block); + + return b; + } + + /// return true if the internal state represents the signature described + /// in the passed in array. + /// + public virtual bool VerifySignature( + byte[] signature) + { + if (emBits < (8 * hLen + 8 * sLen + 9)) + { + return false; + } + + digest.DoFinal(mDash, mDash.Length - hLen - sLen); + + byte[] b = cipher.ProcessBlock(signature, 0, signature.Length); + b.CopyTo(block, block.Length - b.Length); + + if (block[block.Length - 1] != trailer) + { + ClearBlock(block); + return false; + } + + byte[] dbMask = MaskGeneratorFunction1(block, block.Length - hLen - 1, hLen, block.Length - hLen - 1); + + for (int i = 0; i != dbMask.Length; i++) + { + block[i] ^= dbMask[i]; + } + + block[0] &= (byte) ((0xff >> ((block.Length * 8) - emBits))); + + for (int i = 0; i != block.Length - hLen - sLen - 2; i++) + { + if (block[i] != 0) + { + ClearBlock(block); + return false; + } + } + + if (block[block.Length - hLen - sLen - 2] != 0x01) + { + ClearBlock(block); + return false; + } + + Array.Copy(block, block.Length - sLen - hLen - 1, mDash, mDash.Length - sLen, sLen); + + digest.BlockUpdate(mDash, 0, mDash.Length); + digest.DoFinal(mDash, mDash.Length - hLen); + + for (int i = block.Length - hLen - 1, j = mDash.Length - hLen; j != mDash.Length; i++, j++) + { + if ((block[i] ^ mDash[j]) != 0) + { + ClearBlock(mDash); + ClearBlock(block); + return false; + } + } + + ClearBlock(mDash); + ClearBlock(block); + + return true; + } + + /// int to octet string. + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint) i >> 24); + sp[1] = (byte)((uint) i >> 16); + sp[2] = (byte)((uint) i >> 8); + sp[3] = (byte)((uint) i >> 0); + } + + /// mask generator function, as described in Pkcs1v2. + private byte[] MaskGeneratorFunction1( + byte[] Z, + int zOff, + int zLen, + int length) + { + byte[] mask = new byte[length]; + byte[] hashBuf = new byte[hLen]; + byte[] C = new byte[4]; + int counter = 0; + + digest.Reset(); + + while (counter < (length / hLen)) + { + ItoOSP(counter, C); + + digest.BlockUpdate(Z, zOff, zLen); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + hashBuf.CopyTo(mask, counter * hLen); + ++counter; + } + + if ((counter * hLen) < length) + { + ItoOSP(counter, C); + + digest.BlockUpdate(Z, zOff, zLen); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hLen, mask.Length - (counter * hLen)); + } + + return mask; + } + } +} diff --git a/src/core/srcbc/crypto/signers/RsaDigestSigner.cs b/src/core/srcbc/crypto/signers/RsaDigestSigner.cs new file mode 100644 index 0000000..4d98425 --- /dev/null +++ b/src/core/srcbc/crypto/signers/RsaDigestSigner.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class RsaDigestSigner + : ISigner + { + private readonly IAsymmetricBlockCipher rsaEngine = new Pkcs1Encoding(new RsaBlindedEngine()); + private readonly AlgorithmIdentifier algId; + private readonly IDigest digest; + private bool forSigning; + + private static readonly Hashtable oidMap = new Hashtable(); + + /// + /// Load oid table. + /// + static RsaDigestSigner() + { + oidMap["RIPEMD128"] = TeleTrusTObjectIdentifiers.RipeMD128; + oidMap["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; + oidMap["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; + + oidMap["SHA-1"] = X509ObjectIdentifiers.IdSha1; + oidMap["SHA-224"] = NistObjectIdentifiers.IdSha224; + oidMap["SHA-256"] = NistObjectIdentifiers.IdSha256; + oidMap["SHA-384"] = NistObjectIdentifiers.IdSha384; + oidMap["SHA-512"] = NistObjectIdentifiers.IdSha512; + + oidMap["MD2"] = PkcsObjectIdentifiers.MD2; + oidMap["MD4"] = PkcsObjectIdentifiers.MD4; + oidMap["MD5"] = PkcsObjectIdentifiers.MD5; + } + + public RsaDigestSigner( + IDigest digest) + { + this.digest = digest; + + algId = new AlgorithmIdentifier( (DerObjectIdentifier)oidMap[digest.AlgorithmName] , DerNull.Instance); + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "withRSA"; } + } + + /** + * Initialise the signer for signing or verification. + * + * @param forSigning true if for signing, false otherwise + * @param param necessary parameters. + */ + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + AsymmetricKeyParameter k; + + if (parameters is ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.IsPrivate) + throw new InvalidKeyException("Signing requires private key."); + + if (!forSigning && k.IsPrivate) + throw new InvalidKeyException("Verification requires public key."); + + Reset(); + + rsaEngine.Init(forSigning, parameters); + } + + /** + * update the internal digest with the byte b + */ + public void Update( + byte input) + { + digest.Update(input); + } + + /** + * update the internal digest with the byte array in + */ + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(input, inOff, length); + } + + /** + * Generate a signature for the message we've been loaded with using + * the key we were initialised with. + */ + public byte[] GenerateSignature() + { + if (!forSigning) + throw new InvalidOperationException("RsaDigestSigner not initialised for signature generation."); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + byte[] data = DerEncode(hash); + return rsaEngine.ProcessBlock(data, 0, data.Length); + } + + /** + * return true if the internal state represents the signature described + * in the passed in array. + */ + public bool VerifySignature( + byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("RsaDigestSigner not initialised for verification"); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + byte[] sig; + byte[] expected; + + try + { + sig = rsaEngine.ProcessBlock(signature, 0, signature.Length); + expected = DerEncode(hash); + } + catch (Exception) + { + return false; + } + + if (sig.Length == expected.Length) + { + for (int i = 0; i < sig.Length; i++) + { + if (sig[i] != expected[i]) + { + return false; + } + } + } + else if (sig.Length == expected.Length - 2) // NULL left out + { + int sigOffset = sig.Length - hash.Length - 2; + int expectedOffset = expected.Length - hash.Length - 2; + + expected[1] -= 2; // adjust lengths + expected[3] -= 2; + + for (int i = 0; i < hash.Length; i++) + { + if (sig[sigOffset + i] != expected[expectedOffset + i]) // check hash + { + return false; + } + } + + for (int i = 0; i < sigOffset; i++) + { + if (sig[i] != expected[i]) // check header less NULL + { + return false; + } + } + } + else + { + return false; + } + + return true; + } + + public void Reset() + { + digest.Reset(); + } + + private byte[] DerEncode( + byte[] hash) + { + DigestInfo dInfo = new DigestInfo(algId, hash); + + return dInfo.GetDerEncoded(); + } + } +} diff --git a/src/core/srcbc/crypto/tls/AlwaysValidVerifyer.cs b/src/core/srcbc/crypto/tls/AlwaysValidVerifyer.cs new file mode 100644 index 0000000..e5b8ac2 --- /dev/null +++ b/src/core/srcbc/crypto/tls/AlwaysValidVerifyer.cs @@ -0,0 +1,23 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// + /// A certificate verifyer, that will always return true. + ///
+	/// DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING.
+	/// 
+ ///
+ public class AlwaysValidVerifyer + : ICertificateVerifyer + { + /// Return true. + public bool IsValid( + X509CertificateStructure[] certs) + { + return true; + } + } +} diff --git a/src/core/srcbc/crypto/tls/ByteQueue.cs b/src/core/srcbc/crypto/tls/ByteQueue.cs new file mode 100644 index 0000000..c9628fd --- /dev/null +++ b/src/core/srcbc/crypto/tls/ByteQueue.cs @@ -0,0 +1,125 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// + /// A queue for bytes. + ///

+ /// This file could be more optimized. + ///

+ ///
+ public class ByteQueue + { + /// The smallest number which can be written as 2^x which is bigger than i. + public static int NextTwoPow( + int i) + { + /* + * This code is based of a lot of code I found on the Internet + * which mostly referenced a book called "Hacking delight". + * + */ + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + return i + 1; + } + + /** + * The initial size for our buffer. + */ + private const int InitBufSize = 1024; + + /** + * The buffer where we store our data. + */ + private byte[] databuf = new byte[ByteQueue.InitBufSize]; + + /** + * How many bytes at the beginning of the buffer are skipped. + */ + private int skipped = 0; + + /** + * How many bytes in the buffer are valid data. + */ + private int available = 0; + + /// Read data from the buffer. + /// The buffer where the read data will be copied to. + /// How many bytes to skip at the beginning of buf. + /// How many bytes to read at all. + /// How many bytes from our data to skip. + public void Read( + byte[] buf, + int offset, + int len, + int skip) + { + if ((available - skip) < len) + { + throw new TlsException("Not enough data to read"); + } + if ((buf.Length - offset) < len) + { + throw new TlsException("Buffer size of " + buf.Length + " is too small for a read of " + len + " bytes"); + } + Array.Copy(databuf, skipped + skip, buf, offset, len); + } + + /// Add some data to our buffer. + /// A byte-array to read data from. + /// How many bytes to skip at the beginning of the array. + /// How many bytes to read from the array. + public void AddData( + byte[] data, + int offset, + int len) + { + if ((skipped + available + len) > databuf.Length) + { + byte[] tmp = new byte[ByteQueue.NextTwoPow(data.Length)]; + Array.Copy(databuf, skipped, tmp, 0, available); + skipped = 0; + databuf = tmp; + } + Array.Copy(data, offset, databuf, skipped + available, len); + available += len; + } + + /// Remove some bytes from our data from the beginning. + /// How many bytes to remove. + public void RemoveData( + int i) + { + if (i > available) + { + throw new TlsException("Cannot remove " + i + " bytes, only got " + available); + } + + /* + * Skip the data. + */ + available -= i; + skipped += i; + + /* + * If more than half of our data is skipped, we will move the data + * in the buffer. + */ + if (skipped > (databuf.Length / 2)) + { + Array.Copy(databuf, skipped, databuf, 0, available); + skipped = 0; + } + } + + /// The number of bytes which are available in this buffer. + public int Available + { + get { return available; } + } + } +} diff --git a/src/core/srcbc/crypto/tls/Certificate.cs b/src/core/srcbc/crypto/tls/Certificate.cs new file mode 100644 index 0000000..a4ab841 --- /dev/null +++ b/src/core/srcbc/crypto/tls/Certificate.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * A representation for a certificate chain as used by an tls server. + */ + public class Certificate + { + /** + * The certificates. + */ + internal X509CertificateStructure[] certs; + + /** + * Parse the ServerCertificate message. + * + * @param is The stream where to parse from. + * @return A Certificate object with the certs, the server has sended. + * @throws IOException If something goes wrong during parsing. + */ + internal static Certificate Parse( + Stream inStr) + { + X509CertificateStructure[] certs; + int left = TlsUtilities.ReadUint24(inStr); + ArrayList tmp = new ArrayList(); + while (left > 0) + { + int size = TlsUtilities.ReadUint24(inStr); + left -= 3 + size; + byte[] buf = new byte[size]; + TlsUtilities.ReadFully(buf, inStr); + MemoryStream bis = new MemoryStream(buf, false); + Asn1InputStream ais = new Asn1InputStream(bis); + Asn1Object o = ais.ReadObject(); + tmp.Add(X509CertificateStructure.GetInstance(o)); +// if (bis.available() > 0) + if (bis.Position < bis.Length) + { + throw new ArgumentException("Sorry, there is garbage data left after the certificate"); + } + } +// certs = new X509CertificateStructure[tmp.size()]; +// for (int i = 0; i < tmp.size(); i++) +// { +// certs[i] = (X509CertificateStructure)tmp.elementAt(i); +// } + certs = (X509CertificateStructure[]) tmp.ToArray(typeof(X509CertificateStructure)); + return new Certificate(certs); + } + + /** + * Private constructure from an cert array. + * + * @param certs The certs the chain should contain. + */ + private Certificate( + X509CertificateStructure[] certs) + { + this.certs = certs; + } + + /// An array which contains the certs, this chain contains. + public X509CertificateStructure[] GetCerts() + { +// X509CertificateStructure[] result = new X509CertificateStructure[certs.Length]; +// Array.Copy(certs, 0, result, 0, certs.Length); +// return result; + return (X509CertificateStructure[]) certs.Clone(); + } + } +} diff --git a/src/core/srcbc/crypto/tls/CombinedHash.cs b/src/core/srcbc/crypto/tls/CombinedHash.cs new file mode 100644 index 0000000..3976b36 --- /dev/null +++ b/src/core/srcbc/crypto/tls/CombinedHash.cs @@ -0,0 +1,70 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// A combined hash, which implements md5(m) || sha1(m). + public class CombinedHash + : IDigest + { + private IDigest md5 = new MD5Digest(); + private IDigest sha1 = new Sha1Digest(); + + /// + public string AlgorithmName + { + get + { + return md5.AlgorithmName + " and " + sha1.AlgorithmName + " for TLS 1.0"; + } + } + + /// + public int GetByteLength() + { + return System.Math.Max(md5.GetByteLength(), sha1.GetByteLength()); + } + + /// + public int GetDigestSize() + { + return md5.GetDigestSize() + sha1.GetDigestSize(); + } + + /// + public void Update( + byte input) + { + md5.Update(input); + sha1.Update(input); + } + + /// + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + md5.BlockUpdate(input, inOff, len); + sha1.BlockUpdate(input, inOff, len); + } + + /// + public int DoFinal( + byte[] output, + int outOff) + { + int i1 = md5.DoFinal(output, outOff); + int i2 = sha1.DoFinal(output, outOff + i1); + return i1 + i2; + } + + /// + public void Reset() + { + md5.Reset(); + sha1.Reset(); + } + } +} diff --git a/src/core/srcbc/crypto/tls/ICertificateVerifyer.cs b/src/core/srcbc/crypto/tls/ICertificateVerifyer.cs new file mode 100644 index 0000000..5f6ae39 --- /dev/null +++ b/src/core/srcbc/crypto/tls/ICertificateVerifyer.cs @@ -0,0 +1,17 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// + /// This should be implemented by any class which can find out, if a given + /// certificate chain is beeing accepted by an client. + /// + public interface ICertificateVerifyer + { + /// The certs, which are part of the chain. + /// True, if the chain is accepted, false otherwise + bool IsValid(X509CertificateStructure[] certs); + } +} diff --git a/src/core/srcbc/crypto/tls/RecordStream.cs b/src/core/srcbc/crypto/tls/RecordStream.cs new file mode 100644 index 0000000..ee83723 --- /dev/null +++ b/src/core/srcbc/crypto/tls/RecordStream.cs @@ -0,0 +1,107 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// An implementation of the TLS 1.0 record layer. + public class RecordStream + { + private TlsProtocolHandler handler; + private Stream inStr; + private Stream outStr; + internal CombinedHash hash1; + internal CombinedHash hash2; + internal TlsCipherSuite readSuite = null; + internal TlsCipherSuite writeSuite = null; + + internal RecordStream( + TlsProtocolHandler handler, + Stream inStr, + Stream outStr) + { + this.handler = handler; + this.inStr = inStr; + this.outStr = outStr; + hash1 = new CombinedHash(); + hash2 = new CombinedHash(); + this.readSuite = new TlsNullCipherSuite(); + this.writeSuite = this.readSuite; + } + + public void ReadData() + { + short type = TlsUtilities.ReadUint8(inStr); + TlsUtilities.CheckVersion(inStr, handler); + int size = TlsUtilities.ReadUint16(inStr); + byte[] buf = DecodeAndVerify(type, inStr, size); + handler.ProcessData(type, buf, 0, buf.Length); + + } + + internal byte[] DecodeAndVerify( + short type, + Stream inStr, + int len) + { + byte[] buf = new byte[len]; + TlsUtilities.ReadFully(buf, inStr); + byte[] result = readSuite.DecodeCiphertext(type, buf, 0, buf.Length, handler); + return result; + } + + internal void WriteMessage( + short type, + byte[] message, + int offset, + int len) + { + if (type == 22) + { + hash1.BlockUpdate(message, offset, len); + hash2.BlockUpdate(message, offset, len); + } + byte[] ciphertext = writeSuite.EncodePlaintext(type, message, offset, len); + byte[] writeMessage = new byte[ciphertext.Length + 5]; + TlsUtilities.WriteUint8(type, writeMessage, 0); + TlsUtilities.WriteUint8((short)3, writeMessage, 1); + TlsUtilities.WriteUint8((short)1, writeMessage, 2); + TlsUtilities.WriteUint16(ciphertext.Length, writeMessage, 3); + Array.Copy(ciphertext, 0, writeMessage, 5, ciphertext.Length); + outStr.Write(writeMessage, 0, writeMessage.Length); + outStr.Flush(); + } + + internal void Close() + { + IOException e = null; + try + { + inStr.Close(); + } + catch (IOException ex) + { + e = ex; + } + + try + { + // NB: This is harmless if outStr == inStr + outStr.Close(); + } + catch (IOException ex) + { + e = ex; + } + + if (e != null) + { + throw e; + } + } + + internal void Flush() + { + outStr.Flush(); + } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsBlockCipherCipherSuite.cs b/src/core/srcbc/crypto/tls/TlsBlockCipherCipherSuite.cs new file mode 100644 index 0000000..004f6ac --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsBlockCipherCipherSuite.cs @@ -0,0 +1,196 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// A generic TLS 1.0 block cipher suite. This can be used for AES or 3DES for example. + public class TlsBlockCipherCipherSuite + : TlsCipherSuite + { + private IBlockCipher encryptCipher; + + private IBlockCipher decryptCipher; + + private IDigest writeDigest; + + private IDigest readDigest; + + private int cipherKeySize; + + private short keyExchange; + + private TlsMac writeMac; + + private TlsMac readMac; + + internal TlsBlockCipherCipherSuite( + IBlockCipher encrypt, + IBlockCipher decrypt, + IDigest writeDigest, + IDigest readDigest, + int cipherKeySize, + short keyExchange) + { + this.encryptCipher = encrypt; + this.decryptCipher = decrypt; + this.writeDigest = writeDigest; + this.readDigest = readDigest; + this.cipherKeySize = cipherKeySize; + this.keyExchange = keyExchange; + } + + internal override void Init(byte[] ms, byte[] cr, byte[] sr) + { + int prfSize = (2 * cipherKeySize) + (2 * writeDigest.GetDigestSize()) + + (2 * encryptCipher.GetBlockSize()); + byte[] key_block = new byte[prfSize]; + byte[] random = new byte[cr.Length + sr.Length]; + Array.Copy(cr, 0, random, sr.Length, cr.Length); + Array.Copy(sr, 0, random, 0, sr.Length); + TlsUtilities.PRF(ms, TlsUtilities.ToByteArray("key expansion"), random, key_block); + + int offset = 0; + + // Init MACs + writeMac = new TlsMac(writeDigest, key_block, offset, writeDigest + .GetDigestSize()); + offset += writeDigest.GetDigestSize(); + readMac = new TlsMac(readDigest, key_block, offset, readDigest + .GetDigestSize()); + offset += readDigest.GetDigestSize(); + + // Init Ciphers + this.initCipher(true, encryptCipher, key_block, cipherKeySize, offset, + offset + (cipherKeySize * 2)); + offset += cipherKeySize; + this.initCipher(false, decryptCipher, key_block, cipherKeySize, offset, + offset + cipherKeySize + decryptCipher.GetBlockSize()); + } + + private void initCipher(bool forEncryption, IBlockCipher cipher, + byte[] key_block, int key_size, int key_offset, int iv_offset) + { + KeyParameter key_parameter = new KeyParameter(key_block, key_offset, + key_size); + ParametersWithIV parameters_with_iv = new ParametersWithIV( + key_parameter, key_block, iv_offset, cipher.GetBlockSize()); + cipher.Init(forEncryption, parameters_with_iv); + } + + internal override byte[] EncodePlaintext( + short type, + byte[] plaintext, + int offset, + int len) + { + int blocksize = encryptCipher.GetBlockSize(); + int paddingsize = blocksize + - ((len + writeMac.Size + 1) % blocksize); + int totalsize = len + writeMac.Size + paddingsize + 1; + byte[] outbuf = new byte[totalsize]; + Array.Copy(plaintext, offset, outbuf, 0, len); + byte[] mac = writeMac.CalculateMac(type, plaintext, offset, len); + Array.Copy(mac, 0, outbuf, len, mac.Length); + int paddoffset = len + mac.Length; + for (int i = 0; i <= paddingsize; i++) + { + outbuf[i + paddoffset] = (byte)paddingsize; + } + for (int i = 0; i < totalsize; i += blocksize) + { + encryptCipher.ProcessBlock(outbuf, i, outbuf, i); + } + return outbuf; + } + + internal override byte[] DecodeCiphertext( + short type, + byte[] ciphertext, + int offset, + int len, + TlsProtocolHandler handler) + { + int blocksize = decryptCipher.GetBlockSize(); + bool decrypterror = false; + + /* + * Decrypt all the ciphertext using the blockcipher + */ + for (int i = 0; i < len; i += blocksize) + { + decryptCipher.ProcessBlock(ciphertext, i + offset, ciphertext, i + + offset); + } + + /* + * Check if padding is correct + */ + int paddingsize = ciphertext[offset + len - 1]; + if (offset + len - 1 - paddingsize < 0) + { + /* + * This would lead to an negativ array index, so this padding + * must be incorrect! + */ + decrypterror = true; + paddingsize = 0; + } + else + { + /* + * Now, check all the padding-bytes. + */ + for (int i = 0; i <= paddingsize; i++) + { + if (ciphertext[offset + len - 1 - i] != paddingsize) + { + /* Wrong padding */ + decrypterror = true; + } + } + } + + /* + * We now don't care if padding verification has failed or not, + * we will calculate the mac to give an attacker no kind of timing + * profile he can use to find out if mac verification failed or + * padding verification failed. + */ + int plaintextlength = len - readMac.Size - paddingsize - 1; + byte[] calculatedMac = readMac.CalculateMac(type, ciphertext, offset, + plaintextlength); + + /* + * Check all bytes in the mac. + */ + for (int i = 0; i < calculatedMac.Length; i++) + { + if (ciphertext[offset + plaintextlength + i] != calculatedMac[i]) + { + decrypterror = true; + } + } + + /* + * Now, it is save to fail. + */ + if (decrypterror) + { + handler.FailWithError(TlsProtocolHandler.AL_fatal, + TlsProtocolHandler.AP_bad_record_mac); + } + byte[] plaintext = new byte[plaintextlength]; + Array.Copy(ciphertext, offset, plaintext, 0, plaintextlength); + return plaintext; + + } + + internal override short KeyExchangeAlgorithm + { + get { return this.keyExchange; } + } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsCipherSuite.cs b/src/core/srcbc/crypto/tls/TlsCipherSuite.cs new file mode 100644 index 0000000..0f6386c --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsCipherSuite.cs @@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// A generic class for ciphersuites in TLS 1.0. + public abstract class TlsCipherSuite + { + internal const short KE_RSA = 1; + internal const short KE_RSA_EXPORT = 2; + internal const short KE_DHE_DSS = 3; + internal const short KE_DHE_DSS_EXPORT = 4; + internal const short KE_DHE_RSA = 5; + internal const short KE_DHE_RSA_EXPORT = 6; + internal const short KE_DH_DSS = 7; + internal const short KE_DH_RSA = 8; + internal const short KE_DH_anon = 9; + + internal abstract void Init(byte[] ms, byte[] cr, byte[] sr); + + internal abstract byte[] EncodePlaintext(short type, byte[] plaintext, int offset, int len); + + internal abstract byte[] DecodeCiphertext(short type, byte[] plaintext, int offset, int len, TlsProtocolHandler handler); + + internal abstract short KeyExchangeAlgorithm { get; } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsCipherSuiteManager.cs b/src/core/srcbc/crypto/tls/TlsCipherSuiteManager.cs new file mode 100644 index 0000000..505841d --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsCipherSuiteManager.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// + /// A manager for ciphersuite. This class does manage all ciphersuites + /// which are used by MicroTLS. + /// + public class TlsCipherSuiteManager + { + private const int TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000a; + private const int TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016; + private const int TLS_RSA_WITH_AES_128_CBC_SHA = 0x002f; + private const int TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033; + private const int TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035; + private const int TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039; + + internal static void WriteCipherSuites( + Stream outStr) + { + TlsUtilities.WriteUint16(2 * 6, outStr); + + TlsUtilities.WriteUint16(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, outStr); + TlsUtilities.WriteUint16(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, outStr); + TlsUtilities.WriteUint16(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, outStr); + + TlsUtilities.WriteUint16(TLS_RSA_WITH_AES_256_CBC_SHA, outStr); + TlsUtilities.WriteUint16(TLS_RSA_WITH_AES_128_CBC_SHA, outStr); + TlsUtilities.WriteUint16(TLS_RSA_WITH_3DES_EDE_CBC_SHA, outStr); + + } + + internal static TlsCipherSuite GetCipherSuite( + int number, + TlsProtocolHandler handler) + { + switch (number) + { + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + return new TlsBlockCipherCipherSuite(new CbcBlockCipher(new DesEdeEngine()), new CbcBlockCipher(new DesEdeEngine()), new Sha1Digest(), new Sha1Digest(), 24, TlsCipherSuite.KE_RSA); + + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + return new TlsBlockCipherCipherSuite(new CbcBlockCipher(new DesEdeEngine()), new CbcBlockCipher(new DesEdeEngine()), new Sha1Digest(), new Sha1Digest(), 24, TlsCipherSuite.KE_DHE_RSA); + + case TLS_RSA_WITH_AES_128_CBC_SHA: + return new TlsBlockCipherCipherSuite(new CbcBlockCipher(new AesFastEngine()), new CbcBlockCipher(new AesFastEngine()), new Sha1Digest(), new Sha1Digest(), 16, TlsCipherSuite.KE_RSA); + + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + return new TlsBlockCipherCipherSuite(new CbcBlockCipher(new AesFastEngine()), new CbcBlockCipher(new AesFastEngine()), new Sha1Digest(), new Sha1Digest(), 16, TlsCipherSuite.KE_DHE_RSA); + + case TLS_RSA_WITH_AES_256_CBC_SHA: + return new TlsBlockCipherCipherSuite(new CbcBlockCipher(new AesFastEngine()), new CbcBlockCipher(new AesFastEngine()), new Sha1Digest(), new Sha1Digest(), 32, TlsCipherSuite.KE_RSA); + + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + return new TlsBlockCipherCipherSuite(new CbcBlockCipher(new AesFastEngine()), new CbcBlockCipher(new AesFastEngine()), new Sha1Digest(), new Sha1Digest(), 32, TlsCipherSuite.KE_DHE_RSA); + + default: + handler.FailWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_handshake_failure); + + /* + * Unreachable Code, failWithError will always throw an exception! + */ + return null; + } + } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsException.cs b/src/core/srcbc/crypto/tls/TlsException.cs new file mode 100644 index 0000000..bc908b6 --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsException : Exception + { + public TlsException() : base() { } + public TlsException(string message) : base(message) { } + public TlsException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsInputStream.cs b/src/core/srcbc/crypto/tls/TlsInputStream.cs new file mode 100644 index 0000000..38408dc --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsInputStream.cs @@ -0,0 +1,42 @@ +using System; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// An input Stream for a TLS 1.0 connection. + // TODO Fix name and make internal once TlsProtocolHandler.TlsInputStream is removed + public class TlsInputStream + : BaseInputStream + { + private readonly TlsProtocolHandler handler; + + internal TlsInputStream( + TlsProtocolHandler handler) + { + this.handler = handler; + } + + public override int Read( + byte[] buf, + int offset, + int len) + { + return this.handler.ReadApplicationData(buf, offset, len); + } + + public override int ReadByte() + { + byte[] buf = new byte[1]; + if (this.Read(buf, 0, 1) <= 0) + return -1; + return buf[0]; + } + + public override void Close() + { + handler.Close(); + base.Close(); + } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsMac.cs b/src/core/srcbc/crypto/tls/TlsMac.cs new file mode 100644 index 0000000..fde1179 --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsMac.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// + /// A generic TLS MAC implementation, which can be used with any kind of + /// IDigest to act as an HMAC. + /// + public class TlsMac + { + private long seqNo; + private HMac mac; + + /** + * Generate a new instance of an TlsMac. + * + * @param digest The digest to use. + * @param key_block A byte-array where the key for this mac is located. + * @param offset The number of bytes to skip, before the key starts in the buffer. + * @param len The length of the key. + */ + internal TlsMac( + IDigest digest, + byte[] key_block, + int offset, + int len) + { + this.mac = new HMac(digest); + KeyParameter param = new KeyParameter(key_block, offset, len); + this.mac.Init(param); + this.seqNo = 0; + } + + /** + * @return The Keysize of the mac. + */ + internal int Size + { + get { return mac.GetMacSize(); } + } + + /** + * Calculate the mac for some given data. + *

+ * TlsMac will keep track of the sequence number internally. + * + * @param type The message type of the message. + * @param message A byte-buffer containing the message. + * @param offset The number of bytes to skip, before the message starts. + * @param len The length of the message. + * @return A new byte-buffer containing the mac value. + */ + internal byte[] CalculateMac( + short type, + byte[] message, + int offset, + int len) + { + try + { + MemoryStream bosMac = new MemoryStream(13 + len); + TlsUtilities.WriteUint64(seqNo++, bosMac); + TlsUtilities.WriteUint8(type, bosMac); + TlsUtilities.WriteVersion(bosMac); + TlsUtilities.WriteUint16(len, bosMac); + bosMac.Write(message, offset, len); + byte[] macData = bosMac.ToArray(); + mac.BlockUpdate(macData, 0, macData.Length); + byte[] result = new byte[mac.GetMacSize()]; + mac.DoFinal(result, 0); + mac.Reset(); + return result; + } + catch (IOException) + { + // This should never happen + throw new InvalidOperationException("Internal error during mac calculation"); + } + } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsNullCipherSuite.cs b/src/core/srcbc/crypto/tls/TlsNullCipherSuite.cs new file mode 100644 index 0000000..25c3f91 --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsNullCipherSuite.cs @@ -0,0 +1,45 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// A NULL CipherSuite in java, this should only be used during handshake. + public class TlsNullCipherSuite + : TlsCipherSuite + { + internal override void Init( + byte[] ms, + byte[] cr, + byte[] sr) + { + throw new TlsException("Sorry, init of TLS_NULL_WITH_NULL_NULL is forbidden"); + } + + internal override byte[] EncodePlaintext( + short type, + byte[] plaintext, + int offset, + int len) + { + byte[] result = new byte[len]; + Array.Copy(plaintext, offset, result, 0, len); + return result; + } + + internal override byte[] DecodeCiphertext( + short type, + byte[] plaintext, + int offset, + int len, + TlsProtocolHandler handler) + { + byte[] result = new byte[len]; + Array.Copy(plaintext, offset, result, 0, len); + return result; + } + + internal override short KeyExchangeAlgorithm + { + get { return 0; } + } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsOutputStream.cs b/src/core/srcbc/crypto/tls/TlsOutputStream.cs new file mode 100644 index 0000000..8dda729 --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsOutputStream.cs @@ -0,0 +1,50 @@ +using System; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// An output Stream for a TLS 1.0 connection. + // TODO Fix name and make internal once TlsProtocolHandler.TlsOuputStream is removed + public class TlsOuputStream + : BaseOutputStream + { + private readonly TlsProtocolHandler handler; + + internal TlsOuputStream( + TlsProtocolHandler handler) + { + this.handler = handler; + } + + public override void Write( + byte[] buf, + int offset, + int len) + { + this.handler.WriteData(buf, offset, len); + } + + [Obsolete("Use version that takes a 'byte' argument")] + public void WriteByte(int arg0) + { + this.Write((byte)arg0); + } + + public override void WriteByte(byte b) + { + this.Write(b); + } + + public override void Close() + { + handler.Close(); + base.Close(); + } + + public override void Flush() + { + handler.Flush(); + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/crypto/tls/TlsProtocolHandler.cs b/src/core/srcbc/crypto/tls/TlsProtocolHandler.cs new file mode 100644 index 0000000..eb860cd --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsProtocolHandler.cs @@ -0,0 +1,1152 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// An implementation of all high level protocols in TLS 1.0. + public class TlsProtocolHandler + { + private const short RL_CHANGE_CIPHER_SPEC = 20; + private const short RL_ALERT = 21; + private const short RL_HANDSHAKE = 22; + private const short RL_APPLICATION_DATA = 23; + + /* + hello_request(0), client_hello(1), server_hello(2), + certificate(11), server_key_exchange (12), + certificate_request(13), server_hello_done(14), + certificate_verify(15), client_key_exchange(16), + finished(20), (255) + */ + + private const short HP_HELLO_REQUEST = 0; + private const short HP_CLIENT_HELLO = 1; + private const short HP_SERVER_HELLO = 2; + private const short HP_CERTIFICATE = 11; + private const short HP_SERVER_KEY_EXCHANGE = 12; + private const short HP_CERTIFICATE_REQUEST = 13; + private const short HP_SERVER_HELLO_DONE = 14; + private const short HP_CERTIFICATE_VERIFY = 15; + private const short HP_CLIENT_KEY_EXCHANGE = 16; + private const short HP_FINISHED = 20; + + /* + * Our Connection states + */ + + private const short CS_CLIENT_HELLO_SEND = 1; + private const short CS_SERVER_HELLO_RECEIVED = 2; + private const short CS_SERVER_CERTIFICATE_RECEIVED = 3; + private const short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4; + private const short CS_CERTIFICATE_REQUEST_RECEIVED = 5; + private const short CS_SERVER_HELLO_DONE_RECEIVED = 6; + private const short CS_CLIENT_KEY_EXCHANGE_SEND = 7; + private const short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 8; + private const short CS_CLIENT_FINISHED_SEND = 9; + private const short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 10; + private const short CS_DONE = 11; + + internal const short AP_close_notify = 0; + internal const short AP_unexpected_message = 10; + internal const short AP_bad_record_mac = 20; + internal const short AP_decryption_failed = 21; + internal const short AP_record_overflow = 22; + internal const short AP_decompression_failure = 30; + internal const short AP_handshake_failure = 40; + internal const short AP_bad_certificate = 42; + internal const short AP_unsupported_certificate = 43; + internal const short AP_certificate_revoked = 44; + internal const short AP_certificate_expired = 45; + internal const short AP_certificate_unknown = 46; + internal const short AP_illegal_parameter = 47; + internal const short AP_unknown_ca = 48; + internal const short AP_access_denied = 49; + internal const short AP_decode_error = 50; + internal const short AP_decrypt_error = 51; + internal const short AP_export_restriction = 60; + internal const short AP_protocol_version = 70; + internal const short AP_insufficient_security = 71; + internal const short AP_internal_error = 80; + internal const short AP_user_canceled = 90; + internal const short AP_no_renegotiation = 100; + + internal const short AL_warning = 1; + internal const short AL_fatal = 2; + + private static readonly byte[] emptybuf = new byte[0]; + + private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack"; + + /* + * Queues for data from some protocols. + */ + + private ByteQueue applicationDataQueue = new ByteQueue(); + private ByteQueue changeCipherSpecQueue = new ByteQueue(); + private ByteQueue alertQueue = new ByteQueue(); + private ByteQueue handshakeQueue = new ByteQueue(); + + /* + * The Record Stream we use + */ + private RecordStream rs; + + private SecureRandom random; + + /* + * The public rsa-key of the server. + */ + private RsaKeyParameters serverRsaKey = null; + + private TlsInputStream tlsInputStream = null; + private TlsOuputStream tlsOutputStream = null; + + private bool closed = false; + private bool failedWithError = false; + private bool appDataReady = false; + + private byte[] clientRandom; + private byte[] serverRandom; + private byte[] ms; + + private TlsCipherSuite choosenCipherSuite = null; + + private BigInteger Yc; + private byte[] pms; + + private ICertificateVerifyer verifyer = null; + + /* + * Both streams can be the same object + */ + public TlsProtocolHandler( + Stream inStr, + Stream outStr) + { + /* + * We use a 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. + * + * Hopefully, 20 bytes in fast mode are good enough. + */ + byte[] seed = new ThreadedSeedGenerator().GenerateSeed(20, true); + + this.random = new SecureRandom(seed); + this.rs = new RecordStream(this, inStr, outStr); + } + + public TlsProtocolHandler( + Stream inStr, + Stream outStr, + SecureRandom sr) + { + this.random = sr; + this.rs = new RecordStream(this, inStr, outStr); + } + + private short connection_state; + + internal void ProcessData( + short protocol, + byte[] buf, + int offset, + int len) + { + /* + * Have a look at the protocol type, and add it to the correct queue. + */ + switch (protocol) + { + case RL_CHANGE_CIPHER_SPEC: + changeCipherSpecQueue.AddData(buf, offset, len); + processChangeCipherSpec(); + break; + case RL_ALERT: + alertQueue.AddData(buf, offset, len); + processAlert(); + break; + case RL_HANDSHAKE: + handshakeQueue.AddData(buf, offset, len); + processHandshake(); + break; + case RL_APPLICATION_DATA: + if (!appDataReady) + { + this.FailWithError(AL_fatal, AP_unexpected_message); + } + applicationDataQueue.AddData(buf, offset, len); + processApplicationData(); + break; + default: + /* + * Uh, we don't know this protocol. + * + * RFC2246 defines on page 13, that we should ignore this. + */ + break; + } + } + + private void processHandshake() + { + bool read; + do + { + read = false; + + /* + * We need the first 4 bytes, they contain type and length of + * the message. + */ + if (handshakeQueue.Available >= 4) + { + byte[] beginning = new byte[4]; + handshakeQueue.Read(beginning, 0, 4, 0); + MemoryStream bis = new MemoryStream(beginning, false); + short type = TlsUtilities.ReadUint8(bis); + int len = TlsUtilities.ReadUint24(bis); + + /* + * Check if we have enough bytes in the buffer to read + * the full message. + */ + if (handshakeQueue.Available >= (len + 4)) + { + /* + * Read the message. + */ + byte[] buf = new byte[len]; + handshakeQueue.Read(buf, 0, len, 4); + handshakeQueue.RemoveData(len + 4); + + /* + * If it is not a finished message, update our hashes + * we prepare for the finish message. + */ + if (type != HP_FINISHED) + { + rs.hash1.BlockUpdate(beginning, 0, 4); + rs.hash2.BlockUpdate(beginning, 0, 4); + rs.hash1.BlockUpdate(buf, 0, len); + rs.hash2.BlockUpdate(buf, 0, len); + } + + /* + * Now, parse the message. + */ + MemoryStream inStr = new MemoryStream(buf, false); + + /* + * Check the type. + */ + switch (type) + { + case HP_CERTIFICATE: + switch (connection_state) + { + case CS_SERVER_HELLO_RECEIVED: + /* + * Parse the certificates. + */ + Certificate cert = Certificate.Parse(inStr); + AssertEmpty(inStr); + + /* + * Verify them. + */ + if (!this.verifyer.IsValid(cert.GetCerts())) + { + this.FailWithError(AL_fatal, AP_user_canceled); + } + + /* + * We only support RSA certificates. Lets hope + * this is one. + */ + RsaPublicKeyStructure rsaKey = null; + try + { + rsaKey = RsaPublicKeyStructure.GetInstance( + cert.certs[0].TbsCertificate.SubjectPublicKeyInfo.GetPublicKey()); + } + catch (Exception) + { + /* + * Sorry, we have to fail ;-( + */ + this.FailWithError(AL_fatal, AP_unsupported_certificate); + } + + /* + * Parse the servers public RSA key. + */ + this.serverRsaKey = new RsaKeyParameters( + false, + rsaKey.Modulus, + rsaKey.PublicExponent); + + connection_state = CS_SERVER_CERTIFICATE_RECEIVED; + read = true; + break; + default: + this.FailWithError(AL_fatal, AP_unexpected_message); + break; + } + break; + case HP_FINISHED: + switch (connection_state) + { + case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED: + /* + * Read the checksum from the finished message, + * it has always 12 bytes. + */ + byte[] receivedChecksum = new byte[12]; + TlsUtilities.ReadFully(receivedChecksum, inStr); + AssertEmpty(inStr); + + /* + * Calculate our own checksum. + */ + byte[] checksum = new byte[12]; + byte[] md5andsha1 = new byte[16 + 20]; + rs.hash2.DoFinal(md5andsha1, 0); + TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("server finished"), md5andsha1, checksum); + + /* + * Compare both checksums. + */ + for (int i = 0; i < receivedChecksum.Length; i++) + { + if (receivedChecksum[i] != checksum[i]) + { + /* + * Wrong checksum in the finished message. + */ + this.FailWithError(AL_fatal, AP_handshake_failure); + } + } + + connection_state = CS_DONE; + + /* + * We are now ready to receive application data. + */ + this.appDataReady = true; + read = true; + break; + default: + this.FailWithError(AL_fatal, AP_unexpected_message); + break; + } + break; + case HP_SERVER_HELLO: + switch (connection_state) + { + case CS_CLIENT_HELLO_SEND: + /* + * Read the server hello message + */ + TlsUtilities.CheckVersion(inStr, this); + + /* + * Read the server random + */ + this.serverRandom = new byte[32]; + TlsUtilities.ReadFully(this.serverRandom, inStr); + + /* + * Currently, we don't support session ids + */ + short sessionIdLength = TlsUtilities.ReadUint8(inStr); + byte[] sessionId = new byte[sessionIdLength]; + TlsUtilities.ReadFully(sessionId, inStr); + + /* + * Find out which ciphersuite the server has + * chosen. If we don't support this ciphersuite, + * the TlsCipherSuiteManager will throw an + * exception. + */ + this.choosenCipherSuite = TlsCipherSuiteManager.GetCipherSuite( + TlsUtilities.ReadUint16(inStr), this); + + /* + * We support only the null compression which + * means no compression. + */ + short compressionMethod = TlsUtilities.ReadUint8(inStr); + if (compressionMethod != 0) + { + this.FailWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_illegal_parameter); + } + AssertEmpty(inStr); + + connection_state = CS_SERVER_HELLO_RECEIVED; + read = true; + break; + default: + this.FailWithError(AL_fatal, AP_unexpected_message); + break; + } + break; + case HP_SERVER_HELLO_DONE: + switch (connection_state) + { + case CS_SERVER_CERTIFICATE_RECEIVED: + case CS_SERVER_KEY_EXCHANGE_RECEIVED: + case CS_CERTIFICATE_REQUEST_RECEIVED: + + // NB: Original code used case label fall-through + if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED) + { + /* + * There was no server key exchange message, check + * that we are doing RSA key exchange. + */ + if (this.choosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_RSA) + { + this.FailWithError(AL_fatal, AP_unexpected_message); + } + } + + AssertEmpty(inStr); + bool isCertReq = (connection_state == CS_CERTIFICATE_REQUEST_RECEIVED); + connection_state = CS_SERVER_HELLO_DONE_RECEIVED; + + if (isCertReq) + { + sendClientCertificate(); + } + + /* + * Send the client key exchange message, depending + * on the key exchange we are using in our + * ciphersuite. + */ + short ke = this.choosenCipherSuite.KeyExchangeAlgorithm; + + switch (ke) + { + case TlsCipherSuite.KE_RSA: + /* + * We are doing RSA key exchange. We will + * choose a pre master secret and send it + * rsa encrypted to the server. + * + * Prepare pre master secret. + */ + pms = new byte[48]; + pms[0] = 3; + pms[1] = 1; + random.NextBytes(pms, 2, 46); + + /* + * Encode the pms and send it to the server. + * + * Prepare an Pkcs1Encoding with good random + * padding. + */ + RsaBlindedEngine rsa = new RsaBlindedEngine(); + Pkcs1Encoding encoding = new Pkcs1Encoding(rsa); + encoding.Init(true, new ParametersWithRandom(this.serverRsaKey, this.random)); + byte[] encrypted = null; + try + { + encrypted = encoding.ProcessBlock(pms, 0, pms.Length); + } + catch (InvalidCipherTextException) + { + /* + * This should never happen, only during decryption. + */ + this.FailWithError(AL_fatal, AP_internal_error); + } + + /* + * Send the encrypted pms. + */ + MemoryStream bos = new MemoryStream(); + TlsUtilities.WriteUint8(HP_CLIENT_KEY_EXCHANGE, bos); + TlsUtilities.WriteUint24(encrypted.Length + 2, bos); + TlsUtilities.WriteUint16(encrypted.Length, bos); + bos.Write(encrypted, 0, encrypted.Length); + byte[] message = bos.ToArray(); + + rs.WriteMessage((short)RL_HANDSHAKE, message, 0, message.Length); + break; + case TlsCipherSuite.KE_DHE_RSA: + /* + * Send the Client Key Exchange message for + * DHE key exchange. + */ + byte[] YcByte = this.Yc.ToByteArray(); + MemoryStream DHbos = new MemoryStream(); + TlsUtilities.WriteUint8(HP_CLIENT_KEY_EXCHANGE, DHbos); + TlsUtilities.WriteUint24(YcByte.Length + 2, DHbos); + TlsUtilities.WriteUint16(YcByte.Length, DHbos); + DHbos.Write(YcByte, 0, YcByte.Length); + byte[] DHmessage = DHbos.ToArray(); + + rs.WriteMessage((short)RL_HANDSHAKE, DHmessage, 0, DHmessage.Length); + + break; + default: + /* + * Problem during handshake, we don't know + * how to handle this key exchange method. + */ + this.FailWithError(AL_fatal, AP_unexpected_message); + break; + + } + + connection_state = CS_CLIENT_KEY_EXCHANGE_SEND; + + /* + * Now, we send change cipher state + */ + byte[] cmessage = new byte[1]; + cmessage[0] = 1; + rs.WriteMessage((short)RL_CHANGE_CIPHER_SPEC, cmessage, 0, cmessage.Length); + + connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND; + + /* + * Calculate the ms + */ + this.ms = new byte[48]; + byte[] randBytes = new byte[clientRandom.Length + serverRandom.Length]; + Array.Copy(clientRandom, 0, randBytes, 0, clientRandom.Length); + Array.Copy(serverRandom, 0, randBytes, clientRandom.Length, serverRandom.Length); + TlsUtilities.PRF(pms, TlsUtilities.ToByteArray("master secret"), randBytes, this.ms); + + /* + * Initialize our cipher suite + */ + rs.writeSuite = this.choosenCipherSuite; + rs.writeSuite.Init(this.ms, clientRandom, serverRandom); + + /* + * Send our finished message. + */ + byte[] checksum = new byte[12]; + byte[] md5andsha1 = new byte[16 + 20]; + rs.hash1.DoFinal(md5andsha1, 0); + TlsUtilities.PRF(this.ms, TlsUtilities.ToByteArray("client finished"), md5andsha1, checksum); + + MemoryStream bos2 = new MemoryStream(); + TlsUtilities.WriteUint8(HP_FINISHED, bos2); + TlsUtilities.WriteUint24(12, bos2); + bos2.Write(checksum, 0, checksum.Length); + byte[] message2 = bos2.ToArray(); + + rs.WriteMessage((short)RL_HANDSHAKE, message2, 0, message2.Length); + + this.connection_state = CS_CLIENT_FINISHED_SEND; + read = true; + break; + default: + this.FailWithError(AL_fatal, AP_handshake_failure); + break; + } + break; + case HP_SERVER_KEY_EXCHANGE: + switch (connection_state) + { + case CS_SERVER_CERTIFICATE_RECEIVED: + /* + * Check that we are doing DHE key exchange + */ + if (this.choosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_DHE_RSA) + { + this.FailWithError(AL_fatal, AP_unexpected_message); + } + + /* + * Parse the Structure + */ + int pLength = TlsUtilities.ReadUint16(inStr); + byte[] pByte = new byte[pLength]; + TlsUtilities.ReadFully(pByte, inStr); + + int gLength = TlsUtilities.ReadUint16(inStr); + byte[] gByte = new byte[gLength]; + TlsUtilities.ReadFully(gByte, inStr); + + int YsLength = TlsUtilities.ReadUint16(inStr); + byte[] YsByte = new byte[YsLength]; + TlsUtilities.ReadFully(YsByte, inStr); + + int sigLength = TlsUtilities.ReadUint16(inStr); + byte[] sigByte = new byte[sigLength]; + TlsUtilities.ReadFully(sigByte, inStr); + + AssertEmpty(inStr); + + /* + * Verify the Signature. + * + * First, calculate the hash. + */ + CombinedHash sigDigest = new CombinedHash(); + MemoryStream signedData = new MemoryStream(); + TlsUtilities.WriteUint16(pLength, signedData); + signedData.Write(pByte, 0, pByte.Length); + TlsUtilities.WriteUint16(gLength, signedData); + signedData.Write(gByte, 0, gByte.Length); + TlsUtilities.WriteUint16(YsLength, signedData); + signedData.Write(YsByte, 0, YsByte.Length); + byte[] signed = signedData.ToArray(); + + sigDigest.BlockUpdate(this.clientRandom, 0, this.clientRandom.Length); + sigDigest.BlockUpdate(this.serverRandom, 0, this.serverRandom.Length); + sigDigest.BlockUpdate(signed, 0, signed.Length); + byte[] hash = new byte[sigDigest.GetDigestSize()]; + sigDigest.DoFinal(hash, 0); + + /* + * Now, do the RSA operation + */ + RsaBlindedEngine rsa = new RsaBlindedEngine(); + Pkcs1Encoding encoding = new Pkcs1Encoding(rsa); + encoding.Init(false, this.serverRsaKey); + + /* + * The data which was signed + */ + byte[] sigHash = null; + + try + { + sigHash = encoding.ProcessBlock(sigByte, 0, sigByte.Length); + } + catch (InvalidCipherTextException) + { + this.FailWithError(AL_fatal, AP_bad_certificate); + } + + /* + * Check if the data which was signed is equal to + * the hash we calculated. + */ + if (sigHash.Length != hash.Length) + { + this.FailWithError(AL_fatal, AP_bad_certificate); + } + + for (int i = 0; i < sigHash.Length; i++) + { + if (sigHash[i] != hash[i]) + { + this.FailWithError(AL_fatal, AP_bad_certificate); + } + } + + /* + * OK, Signature was correct. + * + * Do the DH calculation. + */ + BigInteger p = new BigInteger(1, pByte); + BigInteger g = new BigInteger(1, gByte); + BigInteger Ys = new BigInteger(1, YsByte); + BigInteger x = new BigInteger(p.BitLength - 1, this.random); + Yc = g.ModPow(x, p); + this.pms = Ys.ModPow(x, p).ToByteArrayUnsigned(); + + this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED; + read = true; + break; + default: + this.FailWithError(AL_fatal, AP_unexpected_message); + break; + } + break; + case HP_CERTIFICATE_REQUEST: + switch (connection_state) + { + case CS_SERVER_CERTIFICATE_RECEIVED: + case CS_SERVER_KEY_EXCHANGE_RECEIVED: + + // NB: Original code used case label fall-through + if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED) + { + /* + * There was no server key exchange message, check + * that we are doing RSA key exchange. + */ + if (this.choosenCipherSuite.KeyExchangeAlgorithm != TlsCipherSuite.KE_RSA) + { + this.FailWithError(AL_fatal, AP_unexpected_message); + } + } + + int typesLength = TlsUtilities.ReadUint8(inStr); + byte[] types = new byte[typesLength]; + TlsUtilities.ReadFully(types, inStr); + + int authsLength = TlsUtilities.ReadUint16(inStr); + byte[] auths = new byte[authsLength]; + TlsUtilities.ReadFully(auths, inStr); + + AssertEmpty(inStr); + + this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED; + read = true; + break; + default: + this.FailWithError(AL_fatal, AP_unexpected_message); + break; + } + break; + case HP_HELLO_REQUEST: + case HP_CLIENT_KEY_EXCHANGE: + case HP_CERTIFICATE_VERIFY: + case HP_CLIENT_HELLO: + default: + // We do not support this! + this.FailWithError(AL_fatal, AP_unexpected_message); + break; + + } + + } + } + } + while (read); + + } + + private void processApplicationData() + { + /* + * There is nothing we need to do here. + * + * This function could be used for callbacks when application + * data arrives in the future. + */ + } + + private void processAlert() + { + while (alertQueue.Available >= 2) + { + /* + * An alert is always 2 bytes. Read the alert. + */ + byte[] tmp = new byte[2]; + alertQueue.Read(tmp, 0, 2, 0); + alertQueue.RemoveData(2); + short level = tmp[0]; + short description = tmp[1]; + if (level == AL_fatal) + { + /* + * This is a fatal error. + */ + this.failedWithError = true; + this.closed = true; + /* + * Now try to Close the stream, ignore errors. + */ + try + { + rs.Close(); + } + catch (Exception) + { + } + throw new IOException(TLS_ERROR_MESSAGE); + } + else + { + /* + * This is just a warning. + */ + if (description == AP_close_notify) + { + /* + * Close notify + */ + this.FailWithError(AL_warning, AP_close_notify); + } + /* + * If it is just a warning, we continue. + */ + } + } + + } + + /** + * This method is called, when a change cipher spec message is received. + * + * @throws IOException If the message has an invalid content or the + * handshake is not in the correct state. + */ + private void processChangeCipherSpec() + { + while (changeCipherSpecQueue.Available > 0) + { + /* + * A change cipher spec message is only one byte with the value 1. + */ + byte[] b = new byte[1]; + changeCipherSpecQueue.Read(b, 0, 1, 0); + changeCipherSpecQueue.RemoveData(1); + if (b[0] != 1) + { + /* + * This should never happen. + */ + this.FailWithError(AL_fatal, AP_unexpected_message); + + } + else + { + /* + * Check if we are in the correct connection state. + */ + if (this.connection_state == CS_CLIENT_FINISHED_SEND) + { + rs.readSuite = rs.writeSuite; + this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED; + } + else + { + /* + * We are not in the correct connection state. + */ + this.FailWithError(AL_fatal, AP_handshake_failure); + } + + } + } + } + + private void sendClientCertificate() + { + /* + * just write back the "no client certificate" message + * see also gnutls, auth_cert.c:643 (0B 00 00 03 00 00 00) + */ + MemoryStream bos = new MemoryStream(); + TlsUtilities.WriteUint8(HP_CERTIFICATE, bos); + TlsUtilities.WriteUint24(3, bos); + TlsUtilities.WriteUint24(0, bos); + byte[] message = bos.ToArray(); + + rs.WriteMessage((short)RL_HANDSHAKE, message, 0, message.Length); + } + + ///

Connects to the remote system. + /// Will be used when a certificate is received to verify + /// that this certificate is accepted by the client. + /// If handshake was not successful + public virtual void Connect( + ICertificateVerifyer verifyer) + { + this.verifyer = verifyer; + + /* + * Send Client hello + * + * First, generate some random data. + */ + this.clientRandom = new byte[32]; + + /* + * TLS 1.0 requires a unix-timestamp in the first 4 bytes + */ + int t = (int)(DateTimeUtilities.CurrentUnixMs() / 1000L); + this.clientRandom[0] = (byte)(t >> 24); + this.clientRandom[1] = (byte)(t >> 16); + this.clientRandom[2] = (byte)(t >> 8); + this.clientRandom[3] = (byte)t; + + random.NextBytes(this.clientRandom, 4, 28); + + + MemoryStream outStr = new MemoryStream(); + TlsUtilities.WriteVersion(outStr); + outStr.Write(this.clientRandom, 0, this.clientRandom.Length); + + /* + * Length of Session id + */ + TlsUtilities.WriteUint8((short)0, outStr); + + /* + * Cipher suites + */ + TlsCipherSuiteManager.WriteCipherSuites(outStr); + + /* + * Compression methods, just the null method. + */ + byte[] compressionMethods = new byte[]{0x00}; + TlsUtilities.WriteUint8((short)compressionMethods.Length, outStr); + outStr.Write(compressionMethods,0, compressionMethods.Length); + + + MemoryStream bos = new MemoryStream(); + TlsUtilities.WriteUint8(HP_CLIENT_HELLO, bos); + TlsUtilities.WriteUint24((int) outStr.Length, bos); + byte[] outBytes = outStr.ToArray(); + bos.Write(outBytes, 0, outBytes.Length); + byte[] message = bos.ToArray(); + rs.WriteMessage(RL_HANDSHAKE, message, 0, message.Length); + connection_state = CS_CLIENT_HELLO_SEND; + + /* + * We will now read data, until we have completed the handshake. + */ + while (connection_state != CS_DONE) + { + rs.ReadData(); + } + + this.tlsInputStream = new TlsInputStream(this); + this.tlsOutputStream = new TlsOuputStream(this); + } + + /** + * Read data from the network. The method will return immed, if there is + * still some data left in the buffer, or block untill some application + * data has been read from the network. + * + * @param buf The buffer where the data will be copied to. + * @param offset The position where the data will be placed in the buffer. + * @param len The maximum number of bytes to read. + * @return The number of bytes read. + * @throws IOException If something goes wrong during reading data. + */ + internal int ReadApplicationData(byte[] buf, int offset, int len) + { + while (applicationDataQueue.Available == 0) + { + /* + * We need to read some data. + */ + if (this.failedWithError) + { + /* + * Something went terribly wrong, we should throw an IOException + */ + throw new IOException(TLS_ERROR_MESSAGE); + } + if (this.closed) + { + /* + * Connection has been closed, there is no more data to read. + */ + return 0; + } + + try + { + rs.ReadData(); + } + catch (IOException e) + { + if (!this.closed) + { + this.FailWithError(AL_fatal, AP_internal_error); + } + throw e; + } + catch (Exception e) + { + if (!this.closed) + { + this.FailWithError(AL_fatal, AP_internal_error); + } + throw e; + } + } + len = System.Math.Min(len, applicationDataQueue.Available); + applicationDataQueue.Read(buf, offset, len, 0); + applicationDataQueue.RemoveData(len); + return len; + } + + /** + * Send some application data to the remote system. + *

+ * The method will handle fragmentation internally. + * + * @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. + * @throws IOException If something goes wrong during sending. + */ + internal void WriteData(byte[] buf, int offset, int len) + { + if (this.failedWithError) + { + throw new IOException(TLS_ERROR_MESSAGE); + } + if (this.closed) + { + throw new IOException("Sorry, connection has been closed, you cannot write more data"); + } + + /* + * Protect against known IV attack! + * + * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT + * YOU ARE DOING HERE. + */ + rs.WriteMessage(RL_APPLICATION_DATA, emptybuf, 0, 0); + + do + { + /* + * We are only allowed to write fragments up to 2^14 bytes. + */ + int toWrite = System.Math.Min(len, 1 << 14); + + try + { + rs.WriteMessage(RL_APPLICATION_DATA, buf, offset, toWrite); + } + catch (IOException e) + { + if (!closed) + { + this.FailWithError(AL_fatal, AP_internal_error); + } + throw e; + } + catch (Exception e) + { + if (!closed) + { + this.FailWithError(AL_fatal, AP_internal_error); + } + throw e; + } + + + offset += toWrite; + len -= toWrite; + } + while (len > 0); + + } + + [Obsolete("Use 'OutputStream' property instead")] + public TlsOuputStream TlsOuputStream + { + get { return this.tlsOutputStream; } + } + + ///

A Stream which can be used to send data. + public virtual Stream OutputStream + { + get { return this.tlsOutputStream; } + } + + [Obsolete("Use 'InputStream' property instead")] + public TlsInputStream TlsInputStream + { + get { return this.tlsInputStream; } + } + + /// A Stream which can be used to read data. + public virtual Stream InputStream + { + get { return this.tlsInputStream; } + } + + /** + * Terminate this connection whith an alert. + *

+ * Can be used for normal closure too. + * + * @param alertLevel The level of the alert, an be AL_fatal or AL_warning. + * @param alertDescription The exact alert message. + * @throws IOException If alert was fatal. + */ + internal void FailWithError( + short alertLevel, + short alertDescription) + { + /* + * Check if the connection is still open. + */ + if (!closed) + { + /* + * Prepare the message + */ + byte[] error = new byte[2]; + error[0] = (byte)alertLevel; + error[1] = (byte)alertDescription; + this.closed = true; + + if (alertLevel == AL_fatal) + { + /* + * This is a fatal message. + */ + this.failedWithError = true; + } + rs.WriteMessage(RL_ALERT, error, 0, 2); + rs.Close(); + if (alertLevel == AL_fatal) + { + throw new IOException(TLS_ERROR_MESSAGE); + } + + } + else + { + throw new IOException(TLS_ERROR_MESSAGE); + } + } + + ///

Closes this connection + /// If something goes wrong during closing. + public virtual void Close() + { + if (!closed) + { + this.FailWithError((short)1, (short)0); + } + } + + /** + * Make sure the Stream is now empty. Fail otherwise. + * + * @param is The Stream to check. + * @throws IOException If is is not empty. + */ + internal void AssertEmpty( + MemoryStream inStr) + { +// if (inStr.available() > 0) + if (inStr.Position < inStr.Length) + { + this.FailWithError(AL_fatal, AP_decode_error); + } + } + + internal void Flush() + { + rs.Flush(); + } + } +} diff --git a/src/core/srcbc/crypto/tls/TlsUtilities.cs b/src/core/srcbc/crypto/tls/TlsUtilities.cs new file mode 100644 index 0000000..c51d3e5 --- /dev/null +++ b/src/core/srcbc/crypto/tls/TlsUtilities.cs @@ -0,0 +1,223 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// Some helper fuctions for MicroTLS. + public class TlsUtilities + { + internal static byte[] ToByteArray(string str) + { + return Strings.ToByteArray(str); + } + + internal static void WriteUint8(short i, Stream os) + { + os.WriteByte((byte)i); + } + + internal static void WriteUint8(short i, byte[] buf, int offset) + { + buf[offset] = (byte)i; + } + + internal static void WriteUint16(int i, Stream os) + { + os.WriteByte((byte)(i >> 8)); + os.WriteByte((byte)i); + } + + internal static void WriteUint16(int i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 8); + buf[offset + 1] = (byte)i; + } + + internal static void WriteUint24(int i, Stream os) + { + os.WriteByte((byte)(i >> 16)); + os.WriteByte((byte)(i >> 8)); + os.WriteByte((byte)i); + } + + internal 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); + } + + internal static void WriteUint32(long i, Stream os) + { + os.WriteByte((byte)(i >> 24)); + os.WriteByte((byte)(i >> 16)); + os.WriteByte((byte)(i >> 8)); + os.WriteByte((byte)i); + } + + internal 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); + } + + internal static void WriteUint64(long i, Stream os) + { + os.WriteByte((byte)(i >> 56)); + os.WriteByte((byte)(i >> 48)); + os.WriteByte((byte)(i >> 40)); + os.WriteByte((byte)(i >> 32)); + os.WriteByte((byte)(i >> 24)); + os.WriteByte((byte)(i >> 16)); + os.WriteByte((byte)(i >> 8)); + os.WriteByte((byte)i); + } + + internal 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); + } + + internal static short ReadUint8(Stream inStr) + { + int i = inStr.ReadByte(); + if (i < 0) + { + throw new EndOfStreamException(); + } + return (short)i; + } + + internal static int ReadUint16(Stream inStr) + { + int i1 = inStr.ReadByte(); + int i2 = inStr.ReadByte(); + if ((i1 | i2) < 0) + { + throw new EndOfStreamException(); + } + return i1 << 8 | i2; + } + + internal static int ReadUint24(Stream inStr) + { + int i1 = inStr.ReadByte(); + int i2 = inStr.ReadByte(); + int i3 = inStr.ReadByte(); + if ((i1 | i2 | i3) < 0) + { + throw new EndOfStreamException(); + } + return (i1 << 16) | (i2 << 8) | i3; + } + + internal static long ReadUint32(Stream inStr) + { + int i1 = inStr.ReadByte(); + int i2 = inStr.ReadByte(); + int i3 = inStr.ReadByte(); + int i4 = inStr.ReadByte(); + if ((i1 | i2 | i3 | i4) < 0) + { + throw new EndOfStreamException(); + } + // TODO Examine this +// return (((long)i1) << 24) | (((long)i2) << 16) | (((long)i3) << 8) | ((long)i4); + return ((long)i1 << 24) | ((long)i2 << 16) | ((long)i3 << 8) | (uint)i4; + } + + internal static void ReadFully(byte[] buf, Stream inStr) + { + if (Streams.ReadFully(inStr, buf, 0, buf.Length) < buf.Length) + throw new EndOfStreamException(); + } + + internal static void CheckVersion(byte[] readVersion, TlsProtocolHandler handler) + { + if ((readVersion[0] != 3) || (readVersion[1] != 1)) + { + handler.FailWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_protocol_version); + } + } + + internal static void CheckVersion(Stream inStr, TlsProtocolHandler handler) + { + int i1 = inStr.ReadByte(); + int i2 = inStr.ReadByte(); + if ((i1 != 3) || (i2 != 1)) + { + handler.FailWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_protocol_version); + } + } + + internal static void WriteVersion(Stream os) + { + os.WriteByte(3); + os.WriteByte(1); + } + + private static void hmac_hash(IDigest digest, byte[] secret, byte[] seed, byte[] output) + { + HMac mac = new HMac(digest); + KeyParameter param = new KeyParameter(secret); + byte[] a = seed; + int size = digest.GetDigestSize(); + int iterations = (output.Length + size - 1) / size; + byte[] buf = new byte[mac.GetMacSize()]; + byte[] buf2 = new byte[mac.GetMacSize()]; + for (int i = 0; i < iterations; i++) + { + mac.Init(param); + mac.BlockUpdate(a, 0, a.Length); + mac.DoFinal(buf, 0); + a = buf; + mac.Init(param); + mac.BlockUpdate(a, 0, a.Length); + mac.BlockUpdate(seed, 0, seed.Length); + mac.DoFinal(buf2, 0); + Array.Copy(buf2, 0, output, (size * i), System.Math.Min(size, output.Length - (size * i))); + } + } + + internal static void PRF( + byte[] secret, + byte[] label, + byte[] seed, + byte[] buf) + { + int s_half = (secret.Length + 1) / 2; + byte[] s1 = new byte[s_half]; + byte[] s2 = new byte[s_half]; + Array.Copy(secret, 0, s1, 0, s_half); + Array.Copy(secret, secret.Length - s_half, s2, 0, s_half); + + byte[] ls = new byte[label.Length + seed.Length]; + Array.Copy(label, 0, ls, 0, label.Length); + Array.Copy(seed, 0, ls, label.Length, seed.Length); + + byte[] prf = new byte[buf.Length]; + hmac_hash(new MD5Digest(), s1, ls, prf); + hmac_hash(new Sha1Digest(), s2, ls, buf); + for (int i = 0; i < buf.Length; i++) + { + buf[i] ^= prf[i]; + } + } + } +} diff --git a/src/core/srcbc/math/BigInteger.cs b/src/core/srcbc/math/BigInteger.cs new file mode 100644 index 0000000..0943f68 --- /dev/null +++ b/src/core/srcbc/math/BigInteger.cs @@ -0,0 +1,3152 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace Org.BouncyCastle.Math +{ +#if !NETCF_1_0 + [Serializable] +#endif + public class BigInteger + { + // The primes b/w 2 and ~2^10 + /* + 3 5 7 11 13 17 19 23 29 + 31 37 41 43 47 53 59 61 67 71 + 73 79 83 89 97 101 103 107 109 113 + 127 131 137 139 149 151 157 163 167 173 + 179 181 191 193 197 199 211 223 227 229 + 233 239 241 251 257 263 269 271 277 281 + 283 293 307 311 313 317 331 337 347 349 + 353 359 367 373 379 383 389 397 401 409 + 419 421 431 433 439 443 449 457 461 463 + 467 479 487 491 499 503 509 521 523 541 + 547 557 563 569 571 577 587 593 599 601 + 607 613 617 619 631 641 643 647 653 659 + 661 673 677 683 691 701 709 719 727 733 + 739 743 751 757 761 769 773 787 797 809 + 811 821 823 827 829 839 853 857 859 863 + 877 881 883 887 907 911 919 929 937 941 + 947 953 967 971 977 983 991 997 + 1009 1013 1019 1021 1031 + */ + + // Each list has a product < 2^31 + private static readonly int[][] primeLists = new int[][] + { + new int[]{ 3, 5, 7, 11, 13, 17, 19, 23 }, + new int[]{ 29, 31, 37, 41, 43 }, + new int[]{ 47, 53, 59, 61, 67 }, + new int[]{ 71, 73, 79, 83 }, + new int[]{ 89, 97, 101, 103 }, + + new int[]{ 107, 109, 113, 127 }, + new int[]{ 131, 137, 139, 149 }, + new int[]{ 151, 157, 163, 167 }, + new int[]{ 173, 179, 181, 191 }, + new int[]{ 193, 197, 199, 211 }, + + new int[]{ 223, 227, 229 }, + new int[]{ 233, 239, 241 }, + new int[]{ 251, 257, 263 }, + new int[]{ 269, 271, 277 }, + new int[]{ 281, 283, 293 }, + + new int[]{ 307, 311, 313 }, + new int[]{ 317, 331, 337 }, + new int[]{ 347, 349, 353 }, + new int[]{ 359, 367, 373 }, + new int[]{ 379, 383, 389 }, + + new int[]{ 397, 401, 409 }, + new int[]{ 419, 421, 431 }, + new int[]{ 433, 439, 443 }, + new int[]{ 449, 457, 461 }, + new int[]{ 463, 467, 479 }, + + new int[]{ 487, 491, 499 }, + new int[]{ 503, 509, 521 }, + new int[]{ 523, 541, 547 }, + new int[]{ 557, 563, 569 }, + new int[]{ 571, 577, 587 }, + + new int[]{ 593, 599, 601 }, + new int[]{ 607, 613, 617 }, + new int[]{ 619, 631, 641 }, + new int[]{ 643, 647, 653 }, + new int[]{ 659, 661, 673 }, + + new int[]{ 677, 683, 691 }, + new int[]{ 701, 709, 719 }, + new int[]{ 727, 733, 739 }, + new int[]{ 743, 751, 757 }, + new int[]{ 761, 769, 773 }, + + new int[]{ 787, 797, 809 }, + new int[]{ 811, 821, 823 }, + new int[]{ 827, 829, 839 }, + new int[]{ 853, 857, 859 }, + new int[]{ 863, 877, 881 }, + + new int[]{ 883, 887, 907 }, + new int[]{ 911, 919, 929 }, + new int[]{ 937, 941, 947 }, + new int[]{ 953, 967, 971 }, + new int[]{ 977, 983, 991 }, + + new int[]{ 997, 1009, 1013 }, + new int[]{ 1019, 1021, 1031 }, + }; + + private static readonly int[] primeProducts; + + private const long IMASK = 0xffffffffL; + private static readonly ulong UIMASK = (ulong)IMASK; + + private static readonly int[] ZeroMagnitude = new int[0]; + private static readonly byte[] ZeroEncoding = new byte[0]; + + public static readonly BigInteger Zero = new BigInteger(0, ZeroMagnitude, false); + public static readonly BigInteger One = createUValueOf(1); + public static readonly BigInteger Two = createUValueOf(2); + public static readonly BigInteger Three = createUValueOf(3); + public static readonly BigInteger Ten = createUValueOf(10); + + private static readonly int chunk2 = 1; // TODO Parse 64 bits at a time + private static readonly BigInteger radix2 = ValueOf(2); + private static readonly BigInteger radix2E = radix2.Pow(chunk2); + + private static readonly int chunk10 = 19; + private static readonly BigInteger radix10 = ValueOf(10); + private static readonly BigInteger radix10E = radix10.Pow(chunk10); + + private static readonly int chunk16 = 16; + private static readonly BigInteger radix16 = ValueOf(16); + private static readonly BigInteger radix16E = radix16.Pow(chunk16); + + private static readonly Random RandomSource = new Random(); + + private const int BitsPerByte = 8; + private const int BitsPerInt = 32; + private const int BytesPerInt = 4; + + static BigInteger() + { + primeProducts = new int[primeLists.Length]; + + for (int i = 0; i < primeLists.Length; ++i) + { + int[] primeList = primeLists[i]; + int product = 1; + for (int j = 0; j < primeList.Length; ++j) + { + product *= primeList[j]; + } + primeProducts[i] = product; + } + } + + private int sign; // -1 means -ve; +1 means +ve; 0 means 0; + private int[] magnitude; // array of ints with [0] being the most significant + private int nBits = -1; // cache BitCount() value + private int nBitLength = -1; // cache calcBitLength() value + private long mQuote = -1L; // -m^(-1) mod b, b = 2^32 (see Montgomery mult.) + + private static int GetByteLength( + int nBits) + { + return (nBits + BitsPerByte - 1) / BitsPerByte; + } + + private BigInteger() + { + } + + private BigInteger( + int signum, + int[] mag, + bool checkMag) + { + if (checkMag) + { + int i = 0; + while (i < mag.Length && mag[i] == 0) + { + ++i; + } + + if (i == mag.Length) + { +// this.sign = 0; + this.magnitude = ZeroMagnitude; + } + else + { + this.sign = signum; + + if (i == 0) + { + this.magnitude = mag; + } + else + { + // strip leading 0 words + this.magnitude = new int[mag.Length - i]; + Array.Copy(mag, i, this.magnitude, 0, this.magnitude.Length); + } + } + } + else + { + this.sign = signum; + this.magnitude = mag; + } + } + + public BigInteger( + string value) + : this(value, 10) + { + } + + public BigInteger( + string str, + int radix) + { + if (str.Length == 0) + throw new FormatException("Zero length BigInteger"); + + NumberStyles style; + int chunk; + BigInteger r; + BigInteger rE; + + switch (radix) + { + case 2: + // Is there anyway to restrict to binary digits? + style = NumberStyles.Integer; + chunk = chunk2; + r = radix2; + rE = radix2E; + break; + case 10: + // This style seems to handle spaces and minus sign already (our processing redundant?) + style = NumberStyles.Integer; + chunk = chunk10; + r = radix10; + rE = radix10E; + break; + case 16: + // TODO Should this be HexNumber? + style = NumberStyles.AllowHexSpecifier; + chunk = chunk16; + r = radix16; + rE = radix16E; + break; + default: + throw new FormatException("Only bases 2, 10, or 16 allowed"); + } + + + int index = 0; + sign = 1; + + if (str[0] == '-') + { + if (str.Length == 1) + throw new FormatException("Zero length BigInteger"); + + sign = -1; + index = 1; + } + + // strip leading zeros from the string str + while (index < str.Length && Int32.Parse(str[index].ToString(), style) == 0) + { + index++; + } + + if (index >= str.Length) + { + // zero value - we're done + sign = 0; + magnitude = ZeroMagnitude; + return; + } + + ////// + // could we work out the max number of ints required to store + // str.Length digits in the given base, then allocate that + // storage in one hit?, then Generate the magnitude in one hit too? + ////// + + BigInteger b = Zero; + + + int next = index + chunk; + + if (next <= str.Length) + { + do + { + string s = str.Substring(index, chunk); + ulong i = ulong.Parse(s, style); + BigInteger bi = createUValueOf(i); + + switch (radix) + { + case 2: + // TODO Need this because we are parsing in radix 10 above + if (i > 1) + throw new FormatException("Bad character in radix 2 string: " + s); + + // TODO Parse 64 bits at a time + b = b.ShiftLeft(1); + break; + case 16: + b = b.ShiftLeft(64); + break; + default: + b = b.Multiply(rE); + break; + } + + b = b.Add(bi); + + index = next; + next += chunk; + } + while (next <= str.Length); + } + + if (index < str.Length) + { + string s = str.Substring(index); + ulong i = ulong.Parse(s, style); + BigInteger bi = createUValueOf(i); + + if (b.sign > 0) + { + if (radix == 2) + { + // NB: Can't reach here since we are parsing one char at a time + Debug.Assert(false); + + // TODO Parse all bits at once +// b = b.ShiftLeft(s.Length); + } + else if (radix == 16) + { + b = b.ShiftLeft(s.Length << 2); + } + else + { + b = b.Multiply(r.Pow(s.Length)); + } + + b = b.Add(bi); + } + else + { + b = bi; + } + } + + // Note: This is the previous (slower) algorithm + // while (index < value.Length) + // { + // char c = value[index]; + // string s = c.ToString(); + // int i = Int32.Parse(s, style); + // + // b = b.Multiply(r).Add(ValueOf(i)); + // index++; + // } + + magnitude = b.magnitude; + } + + public BigInteger( + byte[] bytes) + : this(bytes, 0, bytes.Length) + { + } + + public BigInteger( + byte[] bytes, + int offset, + int length) + { + if (length == 0) + throw new FormatException("Zero length BigInteger"); + + // TODO Move this processing into MakeMagnitude (provide sign argument) + if ((sbyte)bytes[offset] < 0) + { + this.sign = -1; + + int end = offset + length; + + int iBval; + // strip leading sign bytes + for (iBval = offset; iBval < end && ((sbyte)bytes[iBval] == -1); iBval++) + { + } + + if (iBval >= end) + { + this.magnitude = One.magnitude; + } + else + { + int numBytes = end - iBval; + byte[] inverse = new byte[numBytes]; + + int index = 0; + while (index < numBytes) + { + inverse[index++] = (byte)~bytes[iBval++]; + } + + Debug.Assert(iBval == end); + + while (inverse[--index] == byte.MaxValue) + { + inverse[index] = byte.MinValue; + } + + inverse[index]++; + + this.magnitude = MakeMagnitude(inverse, 0, inverse.Length); + } + } + else + { + // strip leading zero bytes and return magnitude bytes + this.magnitude = MakeMagnitude(bytes, offset, length); + this.sign = this.magnitude.Length > 0 ? 1 : 0; + } + } + + private static int[] MakeMagnitude( + byte[] bytes, + int offset, + int length) + { + int end = offset + length; + + // strip leading zeros + int firstSignificant; + for (firstSignificant = offset; firstSignificant < end + && bytes[firstSignificant] == 0; firstSignificant++) + { + } + + if (firstSignificant >= end) + { + return ZeroMagnitude; + } + + int nInts = (end - firstSignificant + 3) / BytesPerInt; + int bCount = (end - firstSignificant) % BytesPerInt; + if (bCount == 0) + { + bCount = BytesPerInt; + } + + if (nInts < 1) + { + return ZeroMagnitude; + } + + int[] mag = new int[nInts]; + + int v = 0; + int magnitudeIndex = 0; + for (int i = firstSignificant; i < end; ++i) + { + v <<= 8; + v |= bytes[i] & 0xff; + bCount--; + if (bCount <= 0) + { + mag[magnitudeIndex] = v; + magnitudeIndex++; + bCount = BytesPerInt; + v = 0; + } + } + + if (magnitudeIndex < mag.Length) + { + mag[magnitudeIndex] = v; + } + + return mag; + } + + public BigInteger( + int sign, + byte[] bytes) + : this(sign, bytes, 0, bytes.Length) + { + } + + public BigInteger( + int sign, + byte[] bytes, + int offset, + int length) + { + if (sign < -1 || sign > 1) + throw new FormatException("Invalid sign value"); + + if (sign == 0) + { + //this.sign = 0; + this.magnitude = ZeroMagnitude; + } + else + { + // copy bytes + this.magnitude = MakeMagnitude(bytes, offset, length); + this.sign = this.magnitude.Length < 1 ? 0 : sign; + } + } + + public BigInteger( + int sizeInBits, + Random random) + { + if (sizeInBits < 0) + throw new ArgumentException("sizeInBits must be non-negative"); + + this.nBits = -1; + this.nBitLength = -1; + + if (sizeInBits == 0) + { +// this.sign = 0; + this.magnitude = ZeroMagnitude; + return; + } + + int nBytes = GetByteLength(sizeInBits); + byte[] b = new byte[nBytes]; + random.NextBytes(b); + + // strip off any excess bits in the MSB + b[0] &= rndMask[BitsPerByte * nBytes - sizeInBits]; + + this.magnitude = MakeMagnitude(b, 0, b.Length); + this.sign = this.magnitude.Length < 1 ? 0 : 1; + } + + private static readonly byte[] rndMask = { 255, 127, 63, 31, 15, 7, 3, 1 }; + + public BigInteger( + int bitLength, + int certainty, + Random random) + { + if (bitLength < 2) + throw new ArithmeticException("bitLength < 2"); + + this.sign = 1; + this.nBitLength = bitLength; + + if (bitLength == 2) + { + this.magnitude = random.Next(2) == 0 + ? Two.magnitude + : Three.magnitude; + return; + } + + int nBytes = GetByteLength(bitLength); + byte[] b = new byte[nBytes]; + + int xBits = BitsPerByte * nBytes - bitLength; + byte mask = rndMask[xBits]; + + for (;;) + { + random.NextBytes(b); + + // strip off any excess bits in the MSB + b[0] &= mask; + + // ensure the leading bit is 1 (to meet the strength requirement) + b[0] |= (byte)(1 << (7 - xBits)); + + // ensure the trailing bit is 1 (i.e. must be odd) + b[nBytes - 1] |= 1; + + this.magnitude = MakeMagnitude(b, 0, b.Length); + this.nBits = -1; + this.mQuote = -1L; + + if (certainty < 1) + break; + + if (CheckProbablePrime(certainty, random)) + break; + + if (bitLength > 32) + { + for (int rep = 0; rep < 10000; ++rep) + { + int n = 33 + random.Next(bitLength - 2); + this.magnitude[this.magnitude.Length - (n >> 5)] ^= (1 << (n & 31)); + this.magnitude[this.magnitude.Length - 1] ^= ((random.Next() + 1) << 1); + this.mQuote = -1L; + + if (CheckProbablePrime(certainty, random)) + return; + } + } + } + } + + public BigInteger Abs() + { + return sign >= 0 ? this : Negate(); + } + + /** + * return a = a + b - b preserved. + */ + private static int[] AddMagnitudes( + int[] a, + int[] b) + { + int tI = a.Length - 1; + int vI = b.Length - 1; + long m = 0; + + while (vI >= 0) + { + m += ((long)(uint)a[tI] + (long)(uint)b[vI--]); + a[tI--] = (int)m; + m = (long)((ulong)m >> 32); + } + + if (m != 0) + { + while (tI >= 0 && ++a[tI--] == 0) + { + } + } + + return a; + } + + public BigInteger Add( + BigInteger value) + { + if (this.sign == 0) + return value; + + if (this.sign != value.sign) + { + if (value.sign == 0) + return this; + + if (value.sign < 0) + return Subtract(value.Negate()); + + return value.Subtract(Negate()); + } + + return AddToMagnitude(value.magnitude); + } + + private BigInteger AddToMagnitude( + int[] magToAdd) + { + int[] big, small; + if (this.magnitude.Length < magToAdd.Length) + { + big = magToAdd; + small = this.magnitude; + } + else + { + big = this.magnitude; + small = magToAdd; + } + + // Conservatively avoid over-allocation when no overflow possible + uint limit = uint.MaxValue; + if (big.Length == small.Length) + limit -= (uint) small[0]; + + bool possibleOverflow = (uint) big[0] >= limit; + + int[] bigCopy; + if (possibleOverflow) + { + bigCopy = new int[big.Length + 1]; + big.CopyTo(bigCopy, 1); + } + else + { + bigCopy = (int[]) big.Clone(); + } + + bigCopy = AddMagnitudes(bigCopy, small); + + return new BigInteger(this.sign, bigCopy, possibleOverflow); + } + + public BigInteger And( + BigInteger value) + { + if (this.sign == 0 || value.sign == 0) + { + return Zero; + } + + int[] aMag = this.sign > 0 + ? this.magnitude + : Add(One).magnitude; + + int[] bMag = value.sign > 0 + ? value.magnitude + : value.Add(One).magnitude; + + bool resultNeg = sign < 0 && value.sign < 0; + int resultLength = System.Math.Max(aMag.Length, bMag.Length); + int[] resultMag = new int[resultLength]; + + int aStart = resultMag.Length - aMag.Length; + int bStart = resultMag.Length - bMag.Length; + + for (int i = 0; i < resultMag.Length; ++i) + { + int aWord = i >= aStart ? aMag[i - aStart] : 0; + int bWord = i >= bStart ? bMag[i - bStart] : 0; + + if (this.sign < 0) + { + aWord = ~aWord; + } + + if (value.sign < 0) + { + bWord = ~bWord; + } + + resultMag[i] = aWord & bWord; + + if (resultNeg) + { + resultMag[i] = ~resultMag[i]; + } + } + + BigInteger result = new BigInteger(1, resultMag, true); + + // TODO Optimise this case + if (resultNeg) + { + result = result.Not(); + } + + return result; + } + + public BigInteger AndNot( + BigInteger val) + { + return And(val.Not()); + } + + public int BitCount + { + get + { + if (nBits == -1) + { + if (sign < 0) + { + // TODO Optimise this case + nBits = Not().BitCount; + } + else + { + int sum = 0; + for (int i = 0; i < magnitude.Length; i++) + { + sum += bitCounts[(byte) magnitude[i]]; + sum += bitCounts[(byte)(magnitude[i] >> 8)]; + sum += bitCounts[(byte)(magnitude[i] >> 16)]; + sum += bitCounts[(byte)(magnitude[i] >> 24)]; + } + nBits = sum; + } + } + + return nBits; + } + } + + private readonly static byte[] bitCounts = + { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, + 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, + 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, + 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, + 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, + 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, + 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, + 6, 6, 7, 6, 7, 7, 8 + }; + + private int calcBitLength( + int indx, + int[] mag) + { + for (;;) + { + if (indx >= mag.Length) + return 0; + + if (mag[indx] != 0) + break; + + ++indx; + } + + // bit length for everything after the first int + int bitLength = 32 * ((mag.Length - indx) - 1); + + // and determine bitlength of first int + int firstMag = mag[indx]; + bitLength += BitLen(firstMag); + + // Check for negative powers of two + if (sign < 0 && ((firstMag & -firstMag) == firstMag)) + { + do + { + if (++indx >= mag.Length) + { + --bitLength; + break; + } + } + while (mag[indx] == 0); + } + + return bitLength; + } + + public int BitLength + { + get + { + if (nBitLength == -1) + { + nBitLength = sign == 0 + ? 0 + : calcBitLength(0, magnitude); + } + + return nBitLength; + } + } + + // + // BitLen(value) is the number of bits in value. + // + private static int BitLen( + int w) + { + // Binary search - decision tree (5 tests, rarely 6) + return (w < 1 << 15 ? (w < 1 << 7 + ? (w < 1 << 3 ? (w < 1 << 1 + ? (w < 1 << 0 ? (w < 0 ? 32 : 0) : 1) + : (w < 1 << 2 ? 2 : 3)) : (w < 1 << 5 + ? (w < 1 << 4 ? 4 : 5) + : (w < 1 << 6 ? 6 : 7))) + : (w < 1 << 11 + ? (w < 1 << 9 ? (w < 1 << 8 ? 8 : 9) : (w < 1 << 10 ? 10 : 11)) + : (w < 1 << 13 ? (w < 1 << 12 ? 12 : 13) : (w < 1 << 14 ? 14 : 15)))) : (w < 1 << 23 ? (w < 1 << 19 + ? (w < 1 << 17 ? (w < 1 << 16 ? 16 : 17) : (w < 1 << 18 ? 18 : 19)) + : (w < 1 << 21 ? (w < 1 << 20 ? 20 : 21) : (w < 1 << 22 ? 22 : 23))) : (w < 1 << 27 + ? (w < 1 << 25 ? (w < 1 << 24 ? 24 : 25) : (w < 1 << 26 ? 26 : 27)) + : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31))))); + } + +// private readonly static byte[] bitLengths = +// { +// 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, +// 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +// 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +// 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +// 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8 +// }; + + private bool QuickPow2Check() + { + return sign > 0 && nBits == 1; + } + + public int CompareTo( + object obj) + { + return CompareTo((BigInteger)obj); + } + + /** + * unsigned comparison on two arrays - note the arrays may + * start with leading zeros. + */ + private static int CompareTo( + int xIndx, + int[] x, + int yIndx, + int[] y) + { + while (xIndx != x.Length && x[xIndx] == 0) + { + xIndx++; + } + + while (yIndx != y.Length && y[yIndx] == 0) + { + yIndx++; + } + + return CompareNoLeadingZeroes(xIndx, x, yIndx, y); + } + + private static int CompareNoLeadingZeroes( + int xIndx, + int[] x, + int yIndx, + int[] y) + { + int diff = (x.Length - y.Length) - (xIndx - yIndx); + + if (diff != 0) + { + return diff < 0 ? -1 : 1; + } + + // lengths of magnitudes the same, test the magnitude values + + while (xIndx < x.Length) + { + uint v1 = (uint)x[xIndx++]; + uint v2 = (uint)y[yIndx++]; + + if (v1 != v2) + return v1 < v2 ? -1 : 1; + } + + return 0; + } + + public int CompareTo( + BigInteger value) + { + return sign < value.sign ? -1 + : sign > value.sign ? 1 + : sign == 0 ? 0 + : sign * CompareNoLeadingZeroes(0, magnitude, 0, value.magnitude); + } + + /** + * return z = x / y - done in place (z value preserved, x contains the + * remainder) + */ + private int[] Divide( + int[] x, + int[] y) + { + int xStart = 0; + while (xStart < x.Length && x[xStart] == 0) + { + ++xStart; + } + + int yStart = 0; + while (yStart < y.Length && y[yStart] == 0) + { + ++yStart; + } + + Debug.Assert(yStart < y.Length); + + int xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y); + int[] count; + + if (xyCmp > 0) + { + int yBitLength = calcBitLength(yStart, y); + int xBitLength = calcBitLength(xStart, x); + int shift = xBitLength - yBitLength; + + int[] iCount; + int iCountStart = 0; + + int[] c; + int cStart = 0; + int cBitLength = yBitLength; + if (shift > 0) + { +// iCount = ShiftLeft(One.magnitude, shift); + iCount = new int[(shift >> 5) + 1]; + iCount[0] = 1 << (shift % 32); + + c = ShiftLeft(y, shift); + cBitLength += shift; + } + else + { + iCount = new int[] { 1 }; + + int len = y.Length - yStart; + c = new int[len]; + Array.Copy(y, yStart, c, 0, len); + } + + count = new int[iCount.Length]; + + for (;;) + { + if (cBitLength < xBitLength + || CompareNoLeadingZeroes(xStart, x, cStart, c) >= 0) + { + Subtract(xStart, x, cStart, c); + AddMagnitudes(count, iCount); + + while (x[xStart] == 0) + { + if (++xStart == x.Length) + return count; + } + + //xBitLength = calcBitLength(xStart, x); + xBitLength = 32 * (x.Length - xStart - 1) + BitLen(x[xStart]); + + if (xBitLength <= yBitLength) + { + if (xBitLength < yBitLength) + return count; + + xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y); + + if (xyCmp <= 0) + break; + } + } + + shift = cBitLength - xBitLength; + + // NB: The case where c[cStart] is 1-bit is harmless + if (shift == 1) + { + uint firstC = (uint) c[cStart] >> 1; + uint firstX = (uint) x[xStart]; + if (firstC > firstX) + ++shift; + } + + if (shift < 2) + { + c = ShiftRightOneInPlace(cStart, c); + --cBitLength; + iCount = ShiftRightOneInPlace(iCountStart, iCount); + } + else + { + c = ShiftRightInPlace(cStart, c, shift); + cBitLength -= shift; + iCount = ShiftRightInPlace(iCountStart, iCount, shift); + } + + //cStart = c.Length - ((cBitLength + 31) / 32); + while (c[cStart] == 0) + { + ++cStart; + } + + while (iCount[iCountStart] == 0) + { + ++iCountStart; + } + } + } + else + { + count = new int[1]; + } + + if (xyCmp == 0) + { + AddMagnitudes(count, One.magnitude); + Array.Clear(x, xStart, x.Length - xStart); + } + + return count; + } + + public BigInteger Divide( + BigInteger val) + { + if (val.sign == 0) + throw new ArithmeticException("Division by zero error"); + + if (sign == 0) + return Zero; + + if (val.QuickPow2Check()) // val is power of two + { + BigInteger result = this.Abs().ShiftRight(val.Abs().BitLength - 1); + return val.sign == this.sign ? result : result.Negate(); + } + + int[] mag = (int[]) this.magnitude.Clone(); + + return new BigInteger(this.sign * val.sign, Divide(mag, val.magnitude), true); + } + + public BigInteger[] DivideAndRemainder( + BigInteger val) + { + if (val.sign == 0) + throw new ArithmeticException("Division by zero error"); + + BigInteger[] biggies = new BigInteger[2]; + + if (sign == 0) + { + biggies[0] = Zero; + biggies[1] = Zero; + } + else if (val.QuickPow2Check()) // val is power of two + { + int e = val.Abs().BitLength - 1; + BigInteger quotient = this.Abs().ShiftRight(e); + int[] remainder = this.LastNBits(e); + + biggies[0] = val.sign == this.sign ? quotient : quotient.Negate(); + biggies[1] = new BigInteger(this.sign, remainder, true); + } + else + { + int[] remainder = (int[]) this.magnitude.Clone(); + int[] quotient = Divide(remainder, val.magnitude); + + biggies[0] = new BigInteger(this.sign * val.sign, quotient, true); + biggies[1] = new BigInteger(this.sign, remainder, true); + } + + return biggies; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + BigInteger biggie = obj as BigInteger; + if (biggie == null) + return false; + + if (biggie.sign != sign || biggie.magnitude.Length != magnitude.Length) + return false; + + for (int i = 0; i < magnitude.Length; i++) + { + if (biggie.magnitude[i] != magnitude[i]) + { + return false; + } + } + + return true; + } + + public BigInteger Gcd( + BigInteger value) + { + if (value.sign == 0) + return Abs(); + + if (sign == 0) + return value.Abs(); + + BigInteger r; + BigInteger u = this; + BigInteger v = value; + + while (v.sign != 0) + { + r = u.Mod(v); + u = v; + v = r; + } + + return u; + } + + public override int GetHashCode() + { + int hc = magnitude.Length; + if (magnitude.Length > 0) + { + hc ^= magnitude[0]; + + if (magnitude.Length > 1) + { + hc ^= magnitude[magnitude.Length - 1]; + } + } + + return sign < 0 ? ~hc : hc; + } + + // TODO Make public? + private BigInteger Inc() + { + if (this.sign == 0) + return One; + + if (this.sign < 0) + return new BigInteger(-1, doSubBigLil(this.magnitude, One.magnitude), true); + + return AddToMagnitude(One.magnitude); + } + + public int IntValue + { + get + { + return sign == 0 ? 0 + : sign > 0 ? magnitude[magnitude.Length - 1] + : -magnitude[magnitude.Length - 1]; + } + } + + /** + * return whether or not a BigInteger is probably prime with a + * probability of 1 - (1/2)**certainty. + *

From Knuth Vol 2, pg 395.

+ */ + public bool IsProbablePrime( + int certainty) + { + if (certainty <= 0) + return true; + + BigInteger n = Abs(); + + if (!n.TestBit(0)) + return n.Equals(Two); + + if (n.Equals(One)) + return false; + + return n.CheckProbablePrime(certainty, RandomSource); + } + + private bool CheckProbablePrime( + int certainty, + Random random) + { + Debug.Assert(certainty > 0); + Debug.Assert(CompareTo(Two) > 0); + Debug.Assert(TestBit(0)); + + + // Try to reduce the penalty for really small numbers + int numLists = System.Math.Min(BitLength - 1, primeLists.Length); + + for (int i = 0; i < numLists; ++i) + { + int test = Remainder(primeProducts[i]); + + int[] primeList = primeLists[i]; + for (int j = 0; j < primeList.Length; ++j) + { + int prime = primeList[j]; + int qRem = test % prime; + if (qRem == 0) + { + // We may find small numbers in the list + return BitLength < 16 && IntValue == prime; + } + } + } + + + // TODO Special case for < 10^16 (RabinMiller fixed list) +// if (BitLength < 30) +// { +// RabinMiller against 2, 3, 5, 7, 11, 13, 23 is sufficient +// } + + + // TODO Is it worth trying to create a hybrid of these two? + return RabinMillerTest(certainty, random); +// return SolovayStrassenTest(certainty, random); + +// bool rbTest = RabinMillerTest(certainty, random); +// bool ssTest = SolovayStrassenTest(certainty, random); +// +// Debug.Assert(rbTest == ssTest); +// +// return rbTest; + } + + internal bool RabinMillerTest( + int certainty, + Random random) + { + Debug.Assert(certainty > 0); + Debug.Assert(BitLength > 2); + Debug.Assert(TestBit(0)); + + // let n = 1 + d . 2^s + BigInteger n = this; + BigInteger nMinusOne = n.Subtract(One); + int s = nMinusOne.GetLowestSetBit(); + BigInteger r = nMinusOne.ShiftRight(s); + + Debug.Assert(s >= 1); + + do + { + // TODO Make a method for random BigIntegers in range 0 < x < n) + // - Method can be optimized by only replacing examined bits at each trial + BigInteger a; + do + { + a = new BigInteger(n.BitLength, random); + } + while (a.CompareTo(One) <= 0 || a.CompareTo(nMinusOne) >= 0); + + BigInteger y = a.ModPow(r, n); + + if (!y.Equals(One)) + { + int j = 0; + while (!y.Equals(nMinusOne)) + { + if (++j == s) + return false; + + y = y.ModPow(Two, n); + + if (y.Equals(One)) + return false; + } + } + + certainty -= 2; // composites pass for only 1/4 possible 'a' + } + while (certainty > 0); + + return true; + } + +// private bool SolovayStrassenTest( +// int certainty, +// Random random) +// { +// Debug.Assert(certainty > 0); +// Debug.Assert(CompareTo(Two) > 0); +// Debug.Assert(TestBit(0)); +// +// BigInteger n = this; +// BigInteger nMinusOne = n.Subtract(One); +// BigInteger e = nMinusOne.ShiftRight(1); +// +// do +// { +// BigInteger a; +// do +// { +// a = new BigInteger(nBitLength, random); +// } +// // NB: Spec says 0 < x < n, but 1 is trivial +// while (a.CompareTo(One) <= 0 || a.CompareTo(n) >= 0); +// +// +// // TODO Check this is redundant given the way Jacobi() works? +//// if (!a.Gcd(n).Equals(One)) +//// return false; +// +// int x = Jacobi(a, n); +// +// if (x == 0) +// return false; +// +// BigInteger check = a.ModPow(e, n); +// +// if (x == 1 && !check.Equals(One)) +// return false; +// +// if (x == -1 && !check.Equals(nMinusOne)) +// return false; +// +// --certainty; +// } +// while (certainty > 0); +// +// return true; +// } +// +// private static int Jacobi( +// BigInteger a, +// BigInteger b) +// { +// Debug.Assert(a.sign >= 0); +// Debug.Assert(b.sign > 0); +// Debug.Assert(b.TestBit(0)); +// Debug.Assert(a.CompareTo(b) < 0); +// +// int totalS = 1; +// for (;;) +// { +// if (a.sign == 0) +// return 0; +// +// if (a.Equals(One)) +// break; +// +// int e = a.GetLowestSetBit(); +// +// int bLsw = b.magnitude[b.magnitude.Length - 1]; +// if ((e & 1) != 0 && ((bLsw & 7) == 3 || (bLsw & 7) == 5)) +// totalS = -totalS; +// +// // TODO Confirm this is faster than later a1.Equals(One) test +// if (a.BitLength == e + 1) +// break; +// BigInteger a1 = a.ShiftRight(e); +//// if (a1.Equals(One)) +//// break; +// +// int a1Lsw = a1.magnitude[a1.magnitude.Length - 1]; +// if ((bLsw & 3) == 3 && (a1Lsw & 3) == 3) +// totalS = -totalS; +// +//// a = b.Mod(a1); +// a = b.Remainder(a1); +// b = a1; +// } +// return totalS; +// } + + public long LongValue + { + get + { + if (sign == 0) + return 0; + + long v; + if (magnitude.Length > 1) + { + v = ((long)magnitude[magnitude.Length - 2] << 32) + | (magnitude[magnitude.Length - 1] & IMASK); + } + else + { + v = (magnitude[magnitude.Length - 1] & IMASK); + } + + return sign < 0 ? -v : v; + } + } + + public BigInteger Max( + BigInteger value) + { + return CompareTo(value) > 0 ? this : value; + } + + public BigInteger Min( + BigInteger value) + { + return CompareTo(value) < 0 ? this : value; + } + + public BigInteger Mod( + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + BigInteger biggie = Remainder(m); + + return (biggie.sign >= 0 ? biggie : biggie.Add(m)); + } + + public BigInteger ModInverse( + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + // TODO Too slow at the moment +// // "Fast Key Exchange with Elliptic Curve Systems" R.Schoeppel +// if (m.TestBit(0)) +// { +// //The Almost Inverse Algorithm +// int k = 0; +// BigInteger B = One, C = Zero, F = this, G = m, tmp; +// +// for (;;) +// { +// // While F is even, do F=F/u, C=C*u, k=k+1. +// int zeroes = F.GetLowestSetBit(); +// if (zeroes > 0) +// { +// F = F.ShiftRight(zeroes); +// C = C.ShiftLeft(zeroes); +// k += zeroes; +// } +// +// // If F = 1, then return B,k. +// if (F.Equals(One)) +// { +// BigInteger half = m.Add(One).ShiftRight(1); +// BigInteger halfK = half.ModPow(BigInteger.ValueOf(k), m); +// return B.Multiply(halfK).Mod(m); +// } +// +// if (F.CompareTo(G) < 0) +// { +// tmp = G; G = F; F = tmp; +// tmp = B; B = C; C = tmp; +// } +// +// F = F.Add(G); +// B = B.Add(C); +// } +// } + + BigInteger x = new BigInteger(); + BigInteger gcd = ExtEuclid(this.Mod(m), m, x, null); + + if (!gcd.Equals(One)) + throw new ArithmeticException("Numbers not relatively prime."); + + if (x.sign < 0) + { + x.sign = 1; + //x = m.Subtract(x); + x.magnitude = doSubBigLil(m.magnitude, x.magnitude); + } + + return x; + } + + /** + * Calculate the numbers u1, u2, and u3 such that: + * + * u1 * a + u2 * b = u3 + * + * where u3 is the greatest common divider of a and b. + * a and b using the extended Euclid algorithm (refer p. 323 + * of The Art of Computer Programming vol 2, 2nd ed). + * This also seems to have the side effect of calculating + * some form of multiplicative inverse. + * + * @param a First number to calculate gcd for + * @param b Second number to calculate gcd for + * @param u1Out the return object for the u1 value + * @param u2Out the return object for the u2 value + * @return The greatest common divisor of a and b + */ + private static BigInteger ExtEuclid( + BigInteger a, + BigInteger b, + BigInteger u1Out, + BigInteger u2Out) + { + BigInteger u1 = One; + BigInteger u3 = a; + BigInteger v1 = Zero; + BigInteger v3 = b; + + while (v3.sign > 0) + { + BigInteger[] q = u3.DivideAndRemainder(v3); + + BigInteger tmp = v1.Multiply(q[0]); + BigInteger tn = u1.Subtract(tmp); + u1 = v1; + v1 = tn; + + u3 = v3; + v3 = q[1]; + } + + if (u1Out != null) + { + u1Out.sign = u1.sign; + u1Out.magnitude = u1.magnitude; + } + + if (u2Out != null) + { + BigInteger tmp = u1.Multiply(a); + tmp = u3.Subtract(tmp); + BigInteger res = tmp.Divide(b); + u2Out.sign = res.sign; + u2Out.magnitude = res.magnitude; + } + + return u3; + } + + private static void ZeroOut( + int[] x) + { + Array.Clear(x, 0, x.Length); + } + + public BigInteger ModPow( + BigInteger exponent, + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + if (m.Equals(One)) + return Zero; + + if (exponent.sign == 0) + return One; + + if (sign == 0) + return Zero; + + int[] zVal = null; + int[] yAccum = null; + int[] yVal; + + // Montgomery exponentiation is only possible if the modulus is odd, + // but AFAIK, this is always the case for crypto algo's + bool useMonty = ((m.magnitude[m.magnitude.Length - 1] & 1) == 1); + long mQ = 0; + if (useMonty) + { + mQ = m.GetMQuote(); + + // tmp = this * R mod m + BigInteger tmp = ShiftLeft(32 * m.magnitude.Length).Mod(m); + zVal = tmp.magnitude; + + useMonty = (zVal.Length <= m.magnitude.Length); + + if (useMonty) + { + yAccum = new int[m.magnitude.Length + 1]; + if (zVal.Length < m.magnitude.Length) + { + int[] longZ = new int[m.magnitude.Length]; + zVal.CopyTo(longZ, longZ.Length - zVal.Length); + zVal = longZ; + } + } + } + + if (!useMonty) + { + if (magnitude.Length <= m.magnitude.Length) + { + //zAccum = new int[m.magnitude.Length * 2]; + zVal = new int[m.magnitude.Length]; + magnitude.CopyTo(zVal, zVal.Length - magnitude.Length); + } + else + { + // + // in normal practice we'll never see this... + // + BigInteger tmp = Remainder(m); + + //zAccum = new int[m.magnitude.Length * 2]; + zVal = new int[m.magnitude.Length]; + tmp.magnitude.CopyTo(zVal, zVal.Length - tmp.magnitude.Length); + } + + yAccum = new int[m.magnitude.Length * 2]; + } + + yVal = new int[m.magnitude.Length]; + + // + // from LSW to MSW + // + for (int i = 0; i < exponent.magnitude.Length; i++) + { + int v = exponent.magnitude[i]; + int bits = 0; + + if (i == 0) + { + while (v > 0) + { + v <<= 1; + bits++; + } + + // + // first time in initialise y + // + zVal.CopyTo(yVal, 0); + + v <<= 1; + bits++; + } + + while (v != 0) + { + if (useMonty) + { + // Montgomery square algo doesn't exist, and a normal + // square followed by a Montgomery reduction proved to + // be almost as heavy as a Montgomery mulitply. + MultiplyMonty(yAccum, yVal, yVal, m.magnitude, mQ); + } + else + { + Square(yAccum, yVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); + ZeroOut(yAccum); + } + bits++; + + if (v < 0) + { + if (useMonty) + { + MultiplyMonty(yAccum, yVal, zVal, m.magnitude, mQ); + } + else + { + Multiply(yAccum, yVal, zVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, + yVal.Length); + ZeroOut(yAccum); + } + } + + v <<= 1; + } + + while (bits < 32) + { + if (useMonty) + { + MultiplyMonty(yAccum, yVal, yVal, m.magnitude, mQ); + } + else + { + Square(yAccum, yVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); + ZeroOut(yAccum); + } + bits++; + } + } + + if (useMonty) + { + // Return y * R^(-1) mod m by doing y * 1 * R^(-1) mod m + ZeroOut(zVal); + zVal[zVal.Length - 1] = 1; + MultiplyMonty(yAccum, yVal, zVal, m.magnitude, mQ); + } + + BigInteger result = new BigInteger(1, yVal, true); + + return exponent.sign > 0 + ? result + : result.ModInverse(m); + } + + /** + * return w with w = x * x - w is assumed to have enough space. + */ + private static int[] Square( + int[] w, + int[] x) + { + // Note: this method allows w to be only (2 * x.Length - 1) words if result will fit +// if (w.Length != 2 * x.Length) +// throw new ArgumentException("no I don't think so..."); + + ulong u1, u2, c; + + int wBase = w.Length - 1; + + for (int i = x.Length - 1; i != 0; i--) + { + ulong v = (ulong)(uint) x[i]; + + u1 = v * v; + u2 = u1 >> 32; + u1 = (uint) u1; + + u1 += (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + c = u2 + (u1 >> 32); + + for (int j = i - 1; j >= 0; j--) + { + --wBase; + u1 = v * (ulong)(uint) x[j]; + u2 = u1 >> 31; // multiply by 2! + u1 = (uint)(u1 << 1); // multiply by 2! + u1 += c + (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + c = u2 + (u1 >> 32); + } + + c += (ulong)(uint) w[--wBase]; + w[wBase] = (int)(uint) c; + + if (--wBase >= 0) + { + w[wBase] = (int)(uint)(c >> 32); + } + else + { + Debug.Assert((uint)(c >> 32) == 0); + } + wBase += i; + } + + u1 = (ulong)(uint) x[0]; + u1 = u1 * u1; + u2 = u1 >> 32; + u1 = u1 & IMASK; + + u1 += (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + if (--wBase >= 0) + { + w[wBase] = (int)(uint)(u2 + (u1 >> 32) + (ulong)(uint) w[wBase]); + } + else + { + Debug.Assert((uint)(u2 + (u1 >> 32)) == 0); + } + + return w; + } + + /** + * return x with x = y * z - x is assumed to have enough space. + */ + private static int[] Multiply( + int[] x, + int[] y, + int[] z) + { + int i = z.Length; + + if (i < 1) + return x; + + int xBase = x.Length - y.Length; + + for (;;) + { + long a = z[--i] & IMASK; + long val = 0; + + for (int j = y.Length - 1; j >= 0; j--) + { + val += a * (y[j] & IMASK) + (x[xBase + j] & IMASK); + + x[xBase + j] = (int)val; + + val = (long)((ulong)val >> 32); + } + + --xBase; + + if (i < 1) + { + if (xBase >= 0) + { + x[xBase] = (int)val; + } + else + { + Debug.Assert(val == 0); + } + break; + } + + x[xBase] = (int)val; + } + + return x; + } + + private static long FastExtEuclid( + long a, + long b, + long[] uOut) + { + long u1 = 1; + long u3 = a; + long v1 = 0; + long v3 = b; + + while (v3 > 0) + { + long q, tn; + + q = u3 / v3; + + tn = u1 - (v1 * q); + u1 = v1; + v1 = tn; + + tn = u3 - (v3 * q); + u3 = v3; + v3 = tn; + } + + uOut[0] = u1; + uOut[1] = (u3 - (u1 * a)) / b; + + return u3; + } + + private static long FastModInverse( + long v, + long m) + { + if (m < 1) + throw new ArithmeticException("Modulus must be positive"); + + long[] x = new long[2]; + long gcd = FastExtEuclid(v, m, x); + + if (gcd != 1) + throw new ArithmeticException("Numbers not relatively prime."); + + if (x[0] < 0) + { + x[0] += m; + } + + return x[0]; + } + +// private static BigInteger MQuoteB = One.ShiftLeft(32); +// private static BigInteger MQuoteBSub1 = MQuoteB.Subtract(One); + + /** + * Calculate mQuote = -m^(-1) mod b with b = 2^32 (32 = word size) + */ + private long GetMQuote() + { + Debug.Assert(this.sign > 0); + + if (mQuote != -1) + { + return mQuote; // already calculated + } + + if (magnitude.Length == 0 || (magnitude[magnitude.Length - 1] & 1) == 0) + { + return -1; // not for even numbers + } + + long v = (((~this.magnitude[this.magnitude.Length - 1]) | 1) & 0xffffffffL); + mQuote = FastModInverse(v, 0x100000000L); + + return mQuote; + } + + /** + * Montgomery multiplication: a = x * y * R^(-1) mod m + *
+ * Based algorithm 14.36 of Handbook of Applied Cryptography. + *
+ *
  • m, x, y should have length n
  • + *
  • a should have length (n + 1)
  • + *
  • b = 2^32, R = b^n
  • + *
    + * The result is put in x + *
    + * NOTE: the indices of x, y, m, a different in HAC and in Java + */ + private static void MultiplyMonty( + int[] a, + int[] x, + int[] y, + int[] m, + long mQuote) + // mQuote = -m^(-1) mod b + { + if (m.Length == 1) + { + x[0] = (int)MultiplyMontyNIsOne((uint)x[0], (uint)y[0], (uint)m[0], (ulong)mQuote); + return; + } + + int n = m.Length; + int nMinus1 = n - 1; + long y_0 = y[nMinus1] & IMASK; + + // 1. a = 0 (Notation: a = (a_{n} a_{n-1} ... a_{0})_{b} ) + Array.Clear(a, 0, n + 1); + + // 2. for i from 0 to (n - 1) do the following: + for (int i = n; i > 0; i--) + { + long x_i = x[i - 1] & IMASK; + + // 2.1 u = ((a[0] + (x[i] * y[0]) * mQuote) mod b + long u = ((((a[n] & IMASK) + ((x_i * y_0) & IMASK)) & IMASK) * mQuote) & IMASK; + + // 2.2 a = (a + x_i * y + u * m) / b + long prod1 = x_i * y_0; + long prod2 = u * (m[nMinus1] & IMASK); + long tmp = (a[n] & IMASK) + (prod1 & IMASK) + (prod2 & IMASK); + long carry = (long)((ulong)prod1 >> 32) + (long)((ulong)prod2 >> 32) + (long)((ulong)tmp >> 32); + for (int j = nMinus1; j > 0; j--) + { + prod1 = x_i * (y[j - 1] & IMASK); + prod2 = u * (m[j - 1] & IMASK); + tmp = (a[j] & IMASK) + (prod1 & IMASK) + (prod2 & IMASK) + (carry & IMASK); + carry = (long)((ulong)carry >> 32) + (long)((ulong)prod1 >> 32) + + (long)((ulong)prod2 >> 32) + (long)((ulong)tmp >> 32); + a[j + 1] = (int)tmp; // division by b + } + carry += (a[0] & IMASK); + a[1] = (int)carry; + a[0] = (int)((ulong)carry >> 32); // OJO!!!!! + } + + // 3. if x >= m the x = x - m + if (CompareTo(0, a, 0, m) >= 0) + { + Subtract(0, a, 0, m); + } + + // put the result in x + Array.Copy(a, 1, x, 0, n); + } + + private static uint MultiplyMontyNIsOne( + uint x, + uint y, + uint m, + ulong mQuote) + { + ulong um = m; + ulong prod1 = (ulong)x * (ulong)y; + ulong u = (prod1 * mQuote) & UIMASK; + ulong prod2 = u * um; + ulong tmp = (prod1 & UIMASK) + (prod2 & UIMASK); + ulong carry = (prod1 >> 32) + (prod2 >> 32) + (tmp >> 32); + + if (carry > um) + { + carry -= um; + } + + return (uint)(carry & UIMASK); + } + + public BigInteger Multiply( + BigInteger val) + { + if (sign == 0 || val.sign == 0) + return Zero; + + if (val.QuickPow2Check()) // val is power of two + { + BigInteger result = this.ShiftLeft(val.Abs().BitLength - 1); + return val.sign > 0 ? result : result.Negate(); + } + + if (this.QuickPow2Check()) // this is power of two + { + BigInteger result = val.ShiftLeft(this.Abs().BitLength - 1); + return this.sign > 0 ? result : result.Negate(); + } + + int maxBitLength = this.BitLength + val.BitLength; + int resLength = (maxBitLength + BitsPerInt - 1) / BitsPerInt; + + int[] res = new int[resLength]; + + if (val == this) + { + Square(res, this.magnitude); + } + else + { + Multiply(res, this.magnitude, val.magnitude); + } + + return new BigInteger(sign * val.sign, res, true); + } + + public BigInteger Negate() + { + if (sign == 0) + return this; + + return new BigInteger(-sign, magnitude, false); + } + + public BigInteger NextProbablePrime() + { + if (sign < 0) + throw new ArithmeticException("Cannot be called on value < 0"); + + if (CompareTo(Two) < 0) + return Two; + + BigInteger n = Inc().SetBit(0); + + while (!n.CheckProbablePrime(100, RandomSource)) + { + n = n.Add(Two); + } + + return n; + } + + public BigInteger Not() + { + return Inc().Negate(); + } + + public BigInteger Pow(int exp) + { + if (exp < 0) + { + throw new ArithmeticException("Negative exponent"); + } + + if (exp == 0) + { + return One; + } + + if (sign == 0 || Equals(One)) + { + return this; + } + + BigInteger y = One; + BigInteger z = this; + + for (;;) + { + if ((exp & 0x1) == 1) + { + y = y.Multiply(z); + } + exp >>= 1; + if (exp == 0) break; + z = z.Multiply(z); + } + + return y; + } + + public static BigInteger ProbablePrime( + int bitLength, + Random random) + { + return new BigInteger(bitLength, 100, random); + } + + private int Remainder( + int m) + { + Debug.Assert(m > 0); + + long acc = 0; + for (int pos = 0; pos < magnitude.Length; ++pos) + { + long posVal = (uint) magnitude[pos]; + acc = (acc << 32 | posVal) % m; + } + + return (int) acc; + } + + /** + * return x = x % y - done in place (y value preserved) + */ + private int[] Remainder( + int[] x, + int[] y) + { + int xStart = 0; + while (xStart < x.Length && x[xStart] == 0) + { + ++xStart; + } + + int yStart = 0; + while (yStart < y.Length && y[yStart] == 0) + { + ++yStart; + } + + Debug.Assert(yStart < y.Length); + + int xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y); + + if (xyCmp > 0) + { + int yBitLength = calcBitLength(yStart, y); + int xBitLength = calcBitLength(xStart, x); + int shift = xBitLength - yBitLength; + + int[] c; + int cStart = 0; + int cBitLength = yBitLength; + if (shift > 0) + { + c = ShiftLeft(y, shift); + cBitLength += shift; + Debug.Assert(c[0] != 0); + } + else + { + int len = y.Length - yStart; + c = new int[len]; + Array.Copy(y, yStart, c, 0, len); + } + + for (;;) + { + if (cBitLength < xBitLength + || CompareNoLeadingZeroes(xStart, x, cStart, c) >= 0) + { + Subtract(xStart, x, cStart, c); + + while (x[xStart] == 0) + { + if (++xStart == x.Length) + return x; + } + + //xBitLength = calcBitLength(xStart, x); + xBitLength = 32 * (x.Length - xStart - 1) + BitLen(x[xStart]); + + if (xBitLength <= yBitLength) + { + if (xBitLength < yBitLength) + return x; + + xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y); + + if (xyCmp <= 0) + break; + } + } + + shift = cBitLength - xBitLength; + + // NB: The case where c[cStart] is 1-bit is harmless + if (shift == 1) + { + uint firstC = (uint) c[cStart] >> 1; + uint firstX = (uint) x[xStart]; + if (firstC > firstX) + ++shift; + } + + if (shift < 2) + { + c = ShiftRightOneInPlace(cStart, c); + --cBitLength; + } + else + { + c = ShiftRightInPlace(cStart, c, shift); + cBitLength -= shift; + } + + //cStart = c.Length - ((cBitLength + 31) / 32); + while (c[cStart] == 0) + { + ++cStart; + } + } + } + + if (xyCmp == 0) + { + Array.Clear(x, xStart, x.Length - xStart); + } + + return x; + } + + public BigInteger Remainder( + BigInteger n) + { + if (n.sign == 0) + throw new ArithmeticException("Division by zero error"); + + if (this.sign == 0) + return Zero; + + // For small values, use fast remainder method + if (n.magnitude.Length == 1) + { + int val = n.magnitude[0]; + + if (val > 0) + { + if (val == 1) + return Zero; + + // TODO Make this func work on uint, and handle val == 1? + int rem = Remainder(val); + + return rem == 0 + ? Zero + : new BigInteger(sign, new int[]{ rem }, false); + } + } + + if (CompareNoLeadingZeroes(0, magnitude, 0, n.magnitude) < 0) + return this; + + int[] result; + if (n.QuickPow2Check()) // n is power of two + { + // TODO Move before small values branch above? + result = LastNBits(n.Abs().BitLength - 1); + } + else + { + result = (int[]) this.magnitude.Clone(); + result = Remainder(result, n.magnitude); + } + + return new BigInteger(sign, result, true); + } + + private int[] LastNBits( + int n) + { + if (n < 1) + return ZeroMagnitude; + + int numWords = (n + BitsPerInt - 1) / BitsPerInt; + numWords = System.Math.Min(numWords, this.magnitude.Length); + int[] result = new int[numWords]; + + Array.Copy(this.magnitude, this.magnitude.Length - numWords, result, 0, numWords); + + int hiBits = n % 32; + if (hiBits != 0) + { + result[0] &= ~(-1 << hiBits); + } + + return result; + } + + /** + * do a left shift - this returns a new array. + */ + private static int[] ShiftLeft( + int[] mag, + int n) + { + int nInts = (int)((uint)n >> 5); + int nBits = n & 0x1f; + int magLen = mag.Length; + int[] newMag; + + if (nBits == 0) + { + newMag = new int[magLen + nInts]; + mag.CopyTo(newMag, 0); + } + else + { + int i = 0; + int nBits2 = 32 - nBits; + int highBits = (int)((uint)mag[0] >> nBits2); + + if (highBits != 0) + { + newMag = new int[magLen + nInts + 1]; + newMag[i++] = highBits; + } + else + { + newMag = new int[magLen + nInts]; + } + + int m = mag[0]; + for (int j = 0; j < magLen - 1; j++) + { + int next = mag[j + 1]; + + newMag[i++] = (m << nBits) | (int)((uint)next >> nBits2); + m = next; + } + + newMag[i] = mag[magLen - 1] << nBits; + } + + return newMag; + } + + public BigInteger ShiftLeft( + int n) + { + if (sign == 0 || magnitude.Length == 0) + return Zero; + + if (n == 0) + return this; + + if (n < 0) + return ShiftRight(-n); + + BigInteger result = new BigInteger(sign, ShiftLeft(magnitude, n), true); + + if (this.nBits != -1) + { + result.nBits = sign > 0 + ? this.nBits + : this.nBits + n; + } + + if (this.nBitLength != -1) + { + result.nBitLength = this.nBitLength + n; + } + + return result; + } + + /** + * do a right shift - this does it in place. + */ + private static int[] ShiftRightInPlace( + int start, + int[] mag, + int n) + { + int nInts = (int)((uint)n >> 5) + start; + int nBits = n & 0x1f; + int magEnd = mag.Length - 1; + + if (nInts != start) + { + int delta = (nInts - start); + + for (int i = magEnd; i >= nInts; i--) + { + mag[i] = mag[i - delta]; + } + for (int i = nInts - 1; i >= start; i--) + { + mag[i] = 0; + } + } + + if (nBits != 0) + { + int nBits2 = 32 - nBits; + int m = mag[magEnd]; + + for (int i = magEnd; i > nInts; --i) + { + int next = mag[i - 1]; + + mag[i] = (int)((uint)m >> nBits) | (next << nBits2); + m = next; + } + + mag[nInts] = (int)((uint)mag[nInts] >> nBits); + } + + return mag; + } + + /** + * do a right shift by one - this does it in place. + */ + private static int[] ShiftRightOneInPlace( + int start, + int[] mag) + { + int i = mag.Length; + int m = mag[i - 1]; + + while (--i > start) + { + int next = mag[i - 1]; + mag[i] = ((int)((uint)m >> 1)) | (next << 31); + m = next; + } + + mag[start] = (int)((uint)mag[start] >> 1); + + return mag; + } + + public BigInteger ShiftRight( + int n) + { + if (n == 0) + return this; + + if (n < 0) + return ShiftLeft(-n); + + if (n >= BitLength) + return (this.sign < 0 ? One.Negate() : Zero); + +// int[] res = (int[]) this.magnitude.Clone(); +// +// res = ShiftRightInPlace(0, res, n); +// +// return new BigInteger(this.sign, res, true); + + int resultLength = (BitLength - n + 31) >> 5; + int[] res = new int[resultLength]; + + int numInts = n >> 5; + int numBits = n & 31; + + if (numBits == 0) + { + Array.Copy(this.magnitude, 0, res, 0, res.Length); + } + else + { + int numBits2 = 32 - numBits; + + int magPos = this.magnitude.Length - 1 - numInts; + for (int i = resultLength - 1; i >= 0; --i) + { + res[i] = (int)((uint) this.magnitude[magPos--] >> numBits); + + if (magPos >= 0) + { + res[i] |= this.magnitude[magPos] << numBits2; + } + } + } + + Debug.Assert(res[0] != 0); + + return new BigInteger(this.sign, res, false); + } + + public int SignValue + { + get { return sign; } + } + + /** + * returns x = x - y - we assume x is >= y + */ + private static int[] Subtract( + int xStart, + int[] x, + int yStart, + int[] y) + { + Debug.Assert(yStart < y.Length); + Debug.Assert(x.Length - xStart >= y.Length - yStart); + + int iT = x.Length; + int iV = y.Length; + long m; + int borrow = 0; + + do + { + m = (x[--iT] & IMASK) - (y[--iV] & IMASK) + borrow; + x[iT] = (int) m; + +// borrow = (m < 0) ? -1 : 0; + borrow = (int)(m >> 63); + } + while (iV > yStart); + + if (borrow != 0) + { + while (--x[--iT] == -1) + { + } + } + + return x; + } + + public BigInteger Subtract( + BigInteger n) + { + if (n.sign == 0) + return this; + + if (this.sign == 0) + return n.Negate(); + + if (this.sign != n.sign) + return Add(n.Negate()); + + int compare = CompareNoLeadingZeroes(0, magnitude, 0, n.magnitude); + if (compare == 0) + return Zero; + + BigInteger bigun, lilun; + if (compare < 0) + { + bigun = n; + lilun = this; + } + else + { + bigun = this; + lilun = n; + } + + return new BigInteger(this.sign * compare, doSubBigLil(bigun.magnitude, lilun.magnitude), true); + } + + private static int[] doSubBigLil( + int[] bigMag, + int[] lilMag) + { + int[] res = (int[]) bigMag.Clone(); + + return Subtract(0, res, 0, lilMag); + } + + public byte[] ToByteArray() + { + return ToByteArray(false); + } + + public byte[] ToByteArrayUnsigned() + { + return ToByteArray(true); + } + + private byte[] ToByteArray( + bool unsigned) + { + if (sign == 0) + return unsigned ? ZeroEncoding : new byte[1]; + + int nBits = (unsigned && sign > 0) + ? BitLength + : BitLength + 1; + + int nBytes = GetByteLength(nBits); + byte[] bytes = new byte[nBytes]; + + int magIndex = magnitude.Length; + int bytesIndex = bytes.Length; + + if (sign > 0) + { + while (magIndex > 1) + { + uint mag = (uint) magnitude[--magIndex]; + bytes[--bytesIndex] = (byte) mag; + bytes[--bytesIndex] = (byte)(mag >> 8); + bytes[--bytesIndex] = (byte)(mag >> 16); + bytes[--bytesIndex] = (byte)(mag >> 24); + } + + uint lastMag = (uint) magnitude[0]; + while (lastMag > byte.MaxValue) + { + bytes[--bytesIndex] = (byte) lastMag; + lastMag >>= 8; + } + + bytes[--bytesIndex] = (byte) lastMag; + } + else // sign < 0 + { + bool carry = true; + + while (magIndex > 1) + { + uint mag = ~((uint) magnitude[--magIndex]); + + if (carry) + { + carry = (++mag == uint.MinValue); + } + + bytes[--bytesIndex] = (byte) mag; + bytes[--bytesIndex] = (byte)(mag >> 8); + bytes[--bytesIndex] = (byte)(mag >> 16); + bytes[--bytesIndex] = (byte)(mag >> 24); + } + + uint lastMag = (uint) magnitude[0]; + + if (carry) + { + // Never wraps because magnitude[0] != 0 + --lastMag; + } + + while (lastMag > byte.MaxValue) + { + bytes[--bytesIndex] = (byte) ~lastMag; + lastMag >>= 8; + } + + bytes[--bytesIndex] = (byte) ~lastMag; + + if (bytesIndex > 0) + { + bytes[--bytesIndex] = byte.MaxValue; + } + } + + return bytes; + } + + public override string ToString() + { + return ToString(10); + } + + public string ToString( + int radix) + { + // TODO Make this method work for other radices (ideally 2 <= radix <= 16) + + switch (radix) + { + case 2: + case 10: + case 16: + break; + default: + throw new FormatException("Only bases 2, 10, 16 are allowed"); + } + + // NB: Can only happen to internally managed instances + if (magnitude == null) + return "null"; + + if (sign == 0) + return "0"; + + Debug.Assert(magnitude.Length > 0); + + StringBuilder sb = new StringBuilder(); + + if (radix == 16) + { + sb.Append(magnitude[0].ToString("x")); + + for (int i = 1; i < magnitude.Length; i++) + { + sb.Append(magnitude[i].ToString("x8")); + } + } + else if (radix == 2) + { + sb.Append('1'); + + for (int i = BitLength - 2; i >= 0; --i) + { + sb.Append(TestBit(i) ? '1' : '0'); + } + } + else + { + // This is algorithm 1a from chapter 4.4 in Seminumerical Algorithms, slow but it works + Stack S = new Stack(); + BigInteger bs = ValueOf(radix); + + // The sign is handled separatly. + // Notice however that for this to work, radix 16 _MUST_ be a special case, + // unless we want to enter a recursion well. In their infinite wisdom, why did not + // the Sun engineers made a c'tor for BigIntegers taking a BigInteger as parameter? + // (Answer: Becuase Sun's BigIntger is clonable, something bouncycastle's isn't.) +// BigInteger u = new BigInteger(Abs().ToString(16), 16); + BigInteger u = this.Abs(); + BigInteger b; + + while (u.sign != 0) + { + b = u.Mod(bs); + if (b.sign == 0) + { + S.Push("0"); + } + else + { + // see how to interact with different bases + S.Push(b.magnitude[0].ToString("d")); + } + u = u.Divide(bs); + } + + // Then pop the stack + while (S.Count != 0) + { + sb.Append((string) S.Pop()); + } + } + + string s = sb.ToString(); + + Debug.Assert(s.Length > 0); + + // Strip leading zeros. (We know this number is not all zeroes though) + if (s[0] == '0') + { + int nonZeroPos = 0; + while (s[++nonZeroPos] == '0') {} + + s = s.Substring(nonZeroPos); + } + + if (sign == -1) + { + s = "-" + s; + } + + return s; + } + + private static BigInteger createUValueOf( + ulong value) + { + int msw = (int)(value >> 32); + int lsw = (int)value; + + if (msw != 0) + return new BigInteger(1, new int[] { msw, lsw }, false); + + if (lsw != 0) + { + BigInteger n = new BigInteger(1, new int[] { lsw }, false); + // Check for a power of two + if ((lsw & -lsw) == lsw) + { + n.nBits = 1; + } + return n; + } + + return Zero; + } + + private static BigInteger createValueOf( + long value) + { + if (value < 0) + { + if (value == long.MinValue) + return createValueOf(~value).Not(); + + return createValueOf(-value).Negate(); + } + + return createUValueOf((ulong)value); + +// // store value into a byte array +// byte[] b = new byte[8]; +// for (int i = 0; i < 8; i++) +// { +// b[7 - i] = (byte)value; +// value >>= 8; +// } +// +// return new BigInteger(b); + } + + public static BigInteger ValueOf( + long value) + { + switch (value) + { + case 0: + return Zero; + case 1: + return One; + case 2: + return Two; + case 3: + return Three; + case 10: + return Ten; + } + + return createValueOf(value); + } + + public int GetLowestSetBit() + { + if (this.sign == 0) + return -1; + + int w = magnitude.Length; + + while (--w > 0) + { + if (magnitude[w] != 0) + break; + } + + int word = (int) magnitude[w]; + Debug.Assert(word != 0); + + int b = (word & 0x0000FFFF) == 0 + ? (word & 0x00FF0000) == 0 + ? 7 + : 15 + : (word & 0x000000FF) == 0 + ? 23 + : 31; + + while (b > 0) + { + if ((word << b) == int.MinValue) + break; + + b--; + } + + return ((magnitude.Length - w) * 32 - (b + 1)); + } + + public bool TestBit( + int n) + { + if (n < 0) + throw new ArithmeticException("Bit position must not be negative"); + + if (sign < 0) + return !Not().TestBit(n); + + int wordNum = n / 32; + if (wordNum >= magnitude.Length) + return false; + + int word = magnitude[magnitude.Length - 1 - wordNum]; + return ((word >> (n % 32)) & 1) > 0; + } + + public BigInteger Or( + BigInteger value) + { + if (this.sign == 0) + return value; + + if (value.sign == 0) + return this; + + int[] aMag = this.sign > 0 + ? this.magnitude + : Add(One).magnitude; + + int[] bMag = value.sign > 0 + ? value.magnitude + : value.Add(One).magnitude; + + bool resultNeg = sign < 0 || value.sign < 0; + int resultLength = System.Math.Max(aMag.Length, bMag.Length); + int[] resultMag = new int[resultLength]; + + int aStart = resultMag.Length - aMag.Length; + int bStart = resultMag.Length - bMag.Length; + + for (int i = 0; i < resultMag.Length; ++i) + { + int aWord = i >= aStart ? aMag[i - aStart] : 0; + int bWord = i >= bStart ? bMag[i - bStart] : 0; + + if (this.sign < 0) + { + aWord = ~aWord; + } + + if (value.sign < 0) + { + bWord = ~bWord; + } + + resultMag[i] = aWord | bWord; + + if (resultNeg) + { + resultMag[i] = ~resultMag[i]; + } + } + + BigInteger result = new BigInteger(1, resultMag, true); + + // TODO Optimise this case + if (resultNeg) + { + result = result.Not(); + } + + return result; + } + + public BigInteger Xor( + BigInteger value) + { + if (this.sign == 0) + return value; + + if (value.sign == 0) + return this; + + int[] aMag = this.sign > 0 + ? this.magnitude + : Add(One).magnitude; + + int[] bMag = value.sign > 0 + ? value.magnitude + : value.Add(One).magnitude; + + // TODO Can just replace with sign != value.sign? + bool resultNeg = (sign < 0 && value.sign >= 0) || (sign >= 0 && value.sign < 0); + int resultLength = System.Math.Max(aMag.Length, bMag.Length); + int[] resultMag = new int[resultLength]; + + int aStart = resultMag.Length - aMag.Length; + int bStart = resultMag.Length - bMag.Length; + + for (int i = 0; i < resultMag.Length; ++i) + { + int aWord = i >= aStart ? aMag[i - aStart] : 0; + int bWord = i >= bStart ? bMag[i - bStart] : 0; + + if (this.sign < 0) + { + aWord = ~aWord; + } + + if (value.sign < 0) + { + bWord = ~bWord; + } + + resultMag[i] = aWord ^ bWord; + + if (resultNeg) + { + resultMag[i] = ~resultMag[i]; + } + } + + BigInteger result = new BigInteger(1, resultMag, true); + + // TODO Optimise this case + if (resultNeg) + { + result = result.Not(); + } + + return result; + } + + public BigInteger SetBit( + int n) + { + if (n < 0) + throw new ArithmeticException("Bit address less than zero"); + + if (TestBit(n)) + return this; + + // TODO Handle negative values and zero + if (sign > 0 && n < (BitLength - 1)) + return FlipExistingBit(n); + + return Or(One.ShiftLeft(n)); + } + + public BigInteger ClearBit( + int n) + { + if (n < 0) + throw new ArithmeticException("Bit address less than zero"); + + if (!TestBit(n)) + return this; + + // TODO Handle negative values + if (sign > 0 && n < (BitLength - 1)) + return FlipExistingBit(n); + + return AndNot(One.ShiftLeft(n)); + } + + public BigInteger FlipBit( + int n) + { + if (n < 0) + throw new ArithmeticException("Bit address less than zero"); + + // TODO Handle negative values and zero + if (sign > 0 && n < (BitLength - 1)) + return FlipExistingBit(n); + + return Xor(One.ShiftLeft(n)); + } + + private BigInteger FlipExistingBit( + int n) + { + Debug.Assert(sign > 0); + Debug.Assert(n >= 0); + Debug.Assert(n < BitLength - 1); + + int[] mag = (int[]) this.magnitude.Clone(); + mag[mag.Length - 1 - (n >> 5)] ^= (1 << (n & 31)); // Flip bit + //mag[mag.Length - 1 - (n / 32)] ^= (1 << (n % 32)); + return new BigInteger(this.sign, mag, false); + } + } +} diff --git a/src/core/srcbc/math/ec/ECAlgorithms.cs b/src/core/srcbc/math/ec/ECAlgorithms.cs new file mode 100644 index 0000000..c37e584 --- /dev/null +++ b/src/core/srcbc/math/ec/ECAlgorithms.cs @@ -0,0 +1,94 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Math.EC +{ + public class ECAlgorithms + { + public static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger a, + ECPoint Q, BigInteger b) + { + ECCurve c = P.Curve; + if (!c.Equals(Q.Curve)) + throw new ArgumentException("P and Q must be on same curve"); + + // TODO Put back in once WTNAF F2m point multiplication is enabled +// // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick +// if (c is F2mCurve) +// { +// F2mCurve f2mCurve = (F2mCurve) c; +// if (f2mCurve.IsKoblitz) +// { +// return P.Multiply(a).Add(Q.Multiply(b)); +// } +// } + + return ImplShamirsTrick(P, a, Q, b); + } + + /* + * "Shamir's Trick", originally due to E. G. Straus + * (Addition chains of vectors. American Mathematical Monthly, + * 71(7):806–808, Aug./Sept. 1964) + * + * Input: The points P, Q, scalar k = (km?, ... , k1, k0) + * and scalar l = (lm?, ... , l1, l0). + * Output: R = k * P + l * Q. + * 1: Z <- P + Q + * 2: R <- O + * 3: for i from m-1 down to 0 do + * 4: R <- R + R {point doubling} + * 5: if (ki = 1) and (li = 0) then R <- R + P end if + * 6: if (ki = 0) and (li = 1) then R <- R + Q end if + * 7: if (ki = 1) and (li = 1) then R <- R + Z end if + * 8: end for + * 9: return R + */ + public static ECPoint ShamirsTrick( + ECPoint P, + BigInteger k, + ECPoint Q, + BigInteger l) + { + if (!P.Curve.Equals(Q.Curve)) + throw new ArgumentException("P and Q must be on same curve"); + + return ImplShamirsTrick(P, k, Q, l); + } + + private static ECPoint ImplShamirsTrick(ECPoint P, BigInteger k, + ECPoint Q, BigInteger l) + { + int m = System.Math.Max(k.BitLength, l.BitLength); + ECPoint Z = P.Add(Q); + ECPoint R = P.Curve.Infinity; + + for (int i = m - 1; i >= 0; --i) + { + R = R.Twice(); + + if (k.TestBit(i)) + { + if (l.TestBit(i)) + { + R = R.Add(Z); + } + else + { + R = R.Add(P); + } + } + else + { + if (l.TestBit(i)) + { + R = R.Add(Q); + } + } + } + + return R; + } + } +} diff --git a/src/core/srcbc/math/ec/ECCurve.cs b/src/core/srcbc/math/ec/ECCurve.cs new file mode 100644 index 0000000..41112c7 --- /dev/null +++ b/src/core/srcbc/math/ec/ECCurve.cs @@ -0,0 +1,661 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Math.EC.Abc; + +namespace Org.BouncyCastle.Math.EC +{ + /// Base class for an elliptic curve. + public abstract class ECCurve + { + internal ECFieldElement a, b; + + public abstract int FieldSize { get; } + public abstract ECFieldElement FromBigInteger(BigInteger x); + public abstract ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression); + public abstract ECPoint DecodePoint(byte[] encoded); + public abstract ECPoint Infinity { get; } + + public ECFieldElement A + { + get { return a; } + } + + public ECFieldElement B + { + get { return b; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECCurve other = obj as ECCurve; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECCurve other) + { + return a.Equals(other.a) && b.Equals(other.b); + } + + public override int GetHashCode() + { + return a.GetHashCode() ^ b.GetHashCode(); + } + } + + public abstract class ECCurveBase : ECCurve + { + protected internal ECCurveBase() + { + } + + protected internal abstract ECPoint DecompressPoint(int yTilde, BigInteger X1); + + /** + * Decode a point on this curve from its ASN.1 encoding. The different + * encodings are taken account of, including point compression for + * Fp (X9.62 s 4.2.1 pg 17). + * @return The decoded point. + */ + public override ECPoint DecodePoint( + byte[] encoded) + { + ECPoint p = null; + int expectedLength = (FieldSize + 7) / 8; + + switch (encoded[0]) + { + case 0x00: // infinity + { + if (encoded.Length != 1) + throw new ArgumentException("Incorrect length for infinity encoding", "encoded"); + + p = Infinity; + break; + } + + case 0x02: // compressed + case 0x03: // compressed + { + if (encoded.Length != (expectedLength + 1)) + throw new ArgumentException("Incorrect length for compressed encoding", "encoded"); + + int yTilde = encoded[0] & 1; + BigInteger X1 = new BigInteger(1, encoded, 1, encoded.Length - 1); + + p = DecompressPoint(yTilde, X1); + break; + } + + case 0x04: // uncompressed + case 0x06: // hybrid + case 0x07: // hybrid + { + if (encoded.Length != (2 * expectedLength + 1)) + throw new ArgumentException("Incorrect length for uncompressed/hybrid encoding", "encoded"); + + BigInteger X1 = new BigInteger(1, encoded, 1, expectedLength); + BigInteger Y1 = new BigInteger(1, encoded, 1 + expectedLength, expectedLength); + + p = CreatePoint(X1, Y1, false); + break; + } + + default: + throw new FormatException("Invalid point encoding " + encoded[0]); + } + + return p; + } + } + + /** + * Elliptic curve over Fp + */ + public class FpCurve : ECCurveBase + { + private readonly BigInteger q; + private readonly FpPoint infinity; + + public FpCurve(BigInteger q, BigInteger a, BigInteger b) + { + this.q = q; + this.a = FromBigInteger(a); + this.b = FromBigInteger(b); + this.infinity = new FpPoint(this, null, null); + } + + public BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new FpFieldElement(this.q, x); + } + + public override ECPoint CreatePoint( + BigInteger X1, + BigInteger Y1, + bool withCompression) + { + // TODO Validation of X1, Y1? + return new FpPoint( + this, + FromBigInteger(X1), + FromBigInteger(Y1), + withCompression); + } + + protected internal override ECPoint DecompressPoint( + int yTilde, + BigInteger X1) + { + ECFieldElement x = FromBigInteger(X1); + ECFieldElement alpha = x.Multiply(x.Square().Add(a)).Add(b); + ECFieldElement beta = alpha.Sqrt(); + + // + // if we can't find a sqrt we haven't got a point on the + // curve - run! + // + if (beta == null) + throw new ArithmeticException("Invalid point compression"); + + BigInteger betaValue = beta.ToBigInteger(); + int bit0 = betaValue.TestBit(0) ? 1 : 0; + + if (bit0 != yTilde) + { + // Use the other root + beta = FromBigInteger(q.Subtract(betaValue)); + } + + return new FpPoint(this, x, beta, true); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + FpCurve other = obj as FpCurve; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + FpCurve other) + { + return base.Equals(other) && q.Equals(other.q); + } + + public override int GetHashCode() + { + return base.GetHashCode() ^ q.GetHashCode(); + } + } + + /** + * Elliptic curves over F2m. The Weierstrass equation is given by + * y2 + xy = x3 + ax2 + b. + */ + public class F2mCurve : ECCurveBase + { + /** + * The exponent m of F2m. + */ + private readonly int m; + + /** + * TPB: The integer k where xm + + * xk + 1 represents the reduction polynomial + * f(z).
    + * PPB: The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + private readonly int k1; + + /** + * TPB: Always set to 0
    + * PPB: The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + private readonly int k2; + + /** + * TPB: Always set to 0
    + * PPB: The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + private readonly int k3; + + /** + * The order of the base point of the curve. + */ + private readonly BigInteger n; + + /** + * The cofactor of the curve. + */ + private readonly BigInteger h; + + /** + * The point at infinity on this curve. + */ + private readonly F2mPoint infinity; + + /** + * The parameter μ of the elliptic curve if this is + * a Koblitz curve. + */ + private sbyte mu = 0; + + /** + * The auxiliary values s0 and + * s1 used for partial modular reduction for + * Koblitz curves. + */ + private BigInteger[] si = null; + + /** + * Constructor for Trinomial Polynomial Basis (TPB). + * @param m The exponent m of + * F2m. + * @param k The integer k where xm + + * xk + 1 represents the reduction + * polynomial f(z). + * @param a The coefficient a in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param b The coefficient b in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + */ + public F2mCurve( + int m, + int k, + BigInteger a, + BigInteger b) + : this(m, k, 0, 0, a, b, null, null) + { + } + + /** + * Constructor for Trinomial Polynomial Basis (TPB). + * @param m The exponent m of + * F2m. + * @param k The integer k where xm + + * xk + 1 represents the reduction + * polynomial f(z). + * @param a The coefficient a in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param b The coefficient b in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param n The order of the main subgroup of the elliptic curve. + * @param h The cofactor of the elliptic curve, i.e. + * #Ea(F2m) = h * n. + */ + public F2mCurve( + int m, + int k, + BigInteger a, + BigInteger b, + BigInteger n, + BigInteger h) + : this(m, k, 0, 0, a, b, n, h) + { + } + + /** + * Constructor for Pentanomial Polynomial Basis (PPB). + * @param m The exponent m of + * F2m. + * @param k1 The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k2 The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k3 The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param a The coefficient a in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param b The coefficient b in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + */ + public F2mCurve( + int m, + int k1, + int k2, + int k3, + BigInteger a, + BigInteger b) + : this(m, k1, k2, k3, a, b, null, null) + { + } + + /** + * Constructor for Pentanomial Polynomial Basis (PPB). + * @param m The exponent m of + * F2m. + * @param k1 The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k2 The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k3 The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param a The coefficient a in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param b The coefficient b in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param n The order of the main subgroup of the elliptic curve. + * @param h The cofactor of the elliptic curve, i.e. + * #Ea(F2m) = h * n. + */ + public F2mCurve( + int m, + int k1, + int k2, + int k3, + BigInteger a, + BigInteger b, + BigInteger n, + BigInteger h) + { + this.m = m; + this.k1 = k1; + this.k2 = k2; + this.k3 = k3; + this.n = n; + this.h = h; + this.infinity = new F2mPoint(this, null, null); + + if (k1 == 0) + throw new ArgumentException("k1 must be > 0"); + + if (k2 == 0) + { + if (k3 != 0) + throw new ArgumentException("k3 must be 0 if k2 == 0"); + } + else + { + if (k2 <= k1) + throw new ArgumentException("k2 must be > k1"); + + if (k3 <= k2) + throw new ArgumentException("k3 must be > k2"); + } + + this.a = FromBigInteger(a); + this.b = FromBigInteger(b); + } + + public override ECPoint Infinity + { + get { return infinity; } + } + + public override int FieldSize + { + get { return m; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, x); + } + + /** + * Returns true if this is a Koblitz curve (ABC curve). + * @return true if this is a Koblitz curve (ABC curve), false otherwise + */ + public bool IsKoblitz + { + get + { + return n != null && h != null + && (a.ToBigInteger().Equals(BigInteger.Zero) + || a.ToBigInteger().Equals(BigInteger.One)) + && b.ToBigInteger().Equals(BigInteger.One); + } + } + + /** + * Returns the parameter μ of the elliptic curve. + * @return μ of the elliptic curve. + * @throws ArgumentException if the given ECCurve is not a + * Koblitz curve. + */ + internal sbyte GetMu() + { + if (mu == 0) + { + lock (this) + { + if (mu == 0) + { + mu = Tnaf.GetMu(this); + } + } + } + + return mu; + } + + /** + * @return the auxiliary values s0 and + * s1 used for partial modular reduction for + * Koblitz curves. + */ + internal BigInteger[] GetSi() + { + if (si == null) + { + lock (this) + { + if (si == null) + { + si = Tnaf.GetSi(this); + } + } + } + return si; + } + + public override ECPoint CreatePoint( + BigInteger X1, + BigInteger Y1, + bool withCompression) + { + // TODO Validation of X1, Y1? + return new F2mPoint( + this, + FromBigInteger(X1), + FromBigInteger(Y1), + withCompression); + } + + protected internal override ECPoint DecompressPoint( + int yTilde, + BigInteger X1) + { + ECFieldElement xp = FromBigInteger(X1); + ECFieldElement yp = null; + if (xp.ToBigInteger().SignValue == 0) + { + yp = (F2mFieldElement)b; + for (int i = 0; i < m - 1; i++) + { + yp = yp.Square(); + } + } + else + { + ECFieldElement beta = xp.Add(a).Add( + b.Multiply(xp.Square().Invert())); + ECFieldElement z = solveQuadradicEquation(beta); + + if (z == null) + throw new ArithmeticException("Invalid point compression"); + + int zBit = z.ToBigInteger().TestBit(0) ? 1 : 0; + if (zBit != yTilde) + { + z = z.Add(FromBigInteger(BigInteger.One)); + } + + yp = xp.Multiply(z); + } + + return new F2mPoint(this, xp, yp, true); + } + + /** + * Solves a quadratic equation z2 + z = beta(X9.62 + * D.1.6) The other solution is z + 1. + * + * @param beta + * The value to solve the qradratic equation for. + * @return the solution for z2 + z = beta or + * null if no solution exists. + */ + private ECFieldElement solveQuadradicEquation(ECFieldElement beta) + { + if (beta.ToBigInteger().SignValue == 0) + { + return FromBigInteger(BigInteger.Zero); + } + + ECFieldElement z = null; + ECFieldElement gamma = FromBigInteger(BigInteger.Zero); + + while (gamma.ToBigInteger().SignValue == 0) + { + ECFieldElement t = FromBigInteger(new BigInteger(m, new Random())); + z = FromBigInteger(BigInteger.Zero); + + ECFieldElement w = beta; + for (int i = 1; i <= m - 1; i++) + { + ECFieldElement w2 = w.Square(); + z = z.Square().Add(w2.Multiply(t)); + w = w2.Add(beta); + } + if (w.ToBigInteger().SignValue != 0) + { + return null; + } + gamma = z.Square().Add(z); + } + return z; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + F2mCurve other = obj as F2mCurve; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + F2mCurve other) + { + return m == other.m + && k1 == other.k1 + && k2 == other.k2 + && k3 == other.k3 + && base.Equals(other); + } + + public override int GetHashCode() + { + return base.GetHashCode() ^ m ^ k1 ^ k2 ^ k3; + } + + public int M + { + get { return m; } + } + + /** + * Return true if curve uses a Trinomial basis. + * + * @return true if curve Trinomial, false otherwise. + */ + public bool IsTrinomial() + { + return k2 == 0 && k3 == 0; + } + + public int K1 + { + get { return k1; } + } + + public int K2 + { + get { return k2; } + } + + public int K3 + { + get { return k3; } + } + + public BigInteger N + { + get { return n; } + } + + public BigInteger H + { + get { return h; } + } + } +} diff --git a/src/core/srcbc/math/ec/ECFieldElement.cs b/src/core/srcbc/math/ec/ECFieldElement.cs new file mode 100644 index 0000000..7a30c14 --- /dev/null +++ b/src/core/srcbc/math/ec/ECFieldElement.cs @@ -0,0 +1,1253 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC +{ + public abstract class ECFieldElement + { + public abstract BigInteger ToBigInteger(); + public abstract string FieldName { get; } + public abstract int FieldSize { get; } + public abstract ECFieldElement Add(ECFieldElement b); + public abstract ECFieldElement Subtract(ECFieldElement b); + public abstract ECFieldElement Multiply(ECFieldElement b); + public abstract ECFieldElement Divide(ECFieldElement b); + public abstract ECFieldElement Negate(); + public abstract ECFieldElement Square(); + public abstract ECFieldElement Invert(); + public abstract ECFieldElement Sqrt(); + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECFieldElement other = obj as ECFieldElement; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECFieldElement other) + { + return ToBigInteger().Equals(other.ToBigInteger()); + } + + public override int GetHashCode() + { + return ToBigInteger().GetHashCode(); + } + + public override string ToString() + { + return this.ToBigInteger().ToString(2); + } + } + + public class FpFieldElement + : ECFieldElement + { + private readonly BigInteger q, x; + + public FpFieldElement( + BigInteger q, + BigInteger x) + { + if (x.CompareTo(q) >= 0) + throw new ArgumentException("x value too large in field element"); + + this.q = q; + this.x = x; + } + + public override BigInteger ToBigInteger() + { + return x; + } + + /** + * return the field name for this field. + * + * @return the string "Fp". + */ + public override string FieldName + { + get { return "Fp"; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public BigInteger Q + { + get { return q; } + } + + public override ECFieldElement Add( + ECFieldElement b) + { + return new FpFieldElement(q, x.Add(b.ToBigInteger()).Mod(q)); + } + + public override ECFieldElement Subtract( + ECFieldElement b) + { + return new FpFieldElement(q, x.Subtract(b.ToBigInteger()).Mod(q)); + } + + public override ECFieldElement Multiply( + ECFieldElement b) + { + return new FpFieldElement(q, x.Multiply(b.ToBigInteger()).Mod(q)); + } + + public override ECFieldElement Divide( + ECFieldElement b) + { + return new FpFieldElement(q, x.Multiply(b.ToBigInteger().ModInverse(q)).Mod(q)); + } + + public override ECFieldElement Negate() + { + return new FpFieldElement(q, x.Negate().Mod(q)); + } + + public override ECFieldElement Square() + { + return new FpFieldElement(q, x.Multiply(x).Mod(q)); + } + + public override ECFieldElement Invert() + { + return new FpFieldElement(q, x.ModInverse(q)); + } + + // D.1.4 91 + /** + * return a sqrt root - the routine verifies that the calculation + * returns the right value - if none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + if (!q.TestBit(0)) + throw Platform.CreateNotImplementedException("even value of q"); + + // p mod 4 == 3 + if (q.TestBit(1)) + { + // TODO Can this be optimised (inline the Square?) + // z = g^(u+1) + p, p = 4u + 3 + ECFieldElement z = new FpFieldElement(q, x.ModPow(q.ShiftRight(2).Add(BigInteger.One), q)); + + return z.Square().Equals(this) ? z : null; + } + + // p mod 4 == 1 + BigInteger qMinusOne = q.Subtract(BigInteger.One); + + BigInteger legendreExponent = qMinusOne.ShiftRight(1); + if (!(x.ModPow(legendreExponent, q).Equals(BigInteger.One))) + return null; + + BigInteger u = qMinusOne.ShiftRight(2); + BigInteger k = u.ShiftLeft(1).Add(BigInteger.One); + + BigInteger Q = this.x; + BigInteger fourQ = Q.ShiftLeft(2).Mod(q); + + BigInteger U, V; + do + { + Random rand = new Random(); + BigInteger P; + do + { + P = new BigInteger(q.BitLength, rand); + } + while (P.CompareTo(q) >= 0 + || !(P.Multiply(P).Subtract(fourQ).ModPow(legendreExponent, q).Equals(qMinusOne))); + + BigInteger[] result = fastLucasSequence(q, P, Q, k); + U = result[0]; + V = result[1]; + + if (V.Multiply(V).Mod(q).Equals(fourQ)) + { + // Integer division by 2, mod q + if (V.TestBit(0)) + { + V = V.Add(q); + } + + V = V.ShiftRight(1); + + Debug.Assert(V.Multiply(V).Mod(q).Equals(x)); + + return new FpFieldElement(q, V); + } + } + while (U.Equals(BigInteger.One) || U.Equals(qMinusOne)); + + return null; + + +// BigInteger qMinusOne = q.Subtract(BigInteger.One); +// +// BigInteger legendreExponent = qMinusOne.ShiftRight(1); +// if (!(x.ModPow(legendreExponent, q).Equals(BigInteger.One))) +// return null; +// +// Random rand = new Random(); +// BigInteger fourX = x.ShiftLeft(2); +// +// BigInteger r; +// do +// { +// r = new BigInteger(q.BitLength, rand); +// } +// while (r.CompareTo(q) >= 0 +// || !(r.Multiply(r).Subtract(fourX).ModPow(legendreExponent, q).Equals(qMinusOne))); +// +// BigInteger n1 = qMinusOne.ShiftRight(2); +// BigInteger n2 = n1.Add(BigInteger.One); +// +// BigInteger wOne = WOne(r, x, q); +// BigInteger wSum = W(n1, wOne, q).Add(W(n2, wOne, q)).Mod(q); +// BigInteger twoR = r.ShiftLeft(1); +// +// BigInteger root = twoR.ModPow(q.Subtract(BigInteger.Two), q) +// .Multiply(x).Mod(q) +// .Multiply(wSum).Mod(q); +// +// return new FpFieldElement(q, root); + } + +// private static BigInteger W(BigInteger n, BigInteger wOne, BigInteger p) +// { +// if (n.Equals(BigInteger.One)) +// return wOne; +// +// bool isEven = !n.TestBit(0); +// n = n.ShiftRight(1); +// if (isEven) +// { +// BigInteger w = W(n, wOne, p); +// return w.Multiply(w).Subtract(BigInteger.Two).Mod(p); +// } +// BigInteger w1 = W(n.Add(BigInteger.One), wOne, p); +// BigInteger w2 = W(n, wOne, p); +// return w1.Multiply(w2).Subtract(wOne).Mod(p); +// } +// +// private BigInteger WOne(BigInteger r, BigInteger x, BigInteger p) +// { +// return r.Multiply(r).Multiply(x.ModPow(q.Subtract(BigInteger.Two), q)).Subtract(BigInteger.Two).Mod(p); +// } + + private static BigInteger[] fastLucasSequence( + BigInteger p, + BigInteger P, + BigInteger Q, + BigInteger k) + { + // TODO Research and apply "common-multiplicand multiplication here" + + int n = k.BitLength; + int s = k.GetLowestSetBit(); + + Debug.Assert(k.TestBit(s)); + + BigInteger Uh = BigInteger.One; + BigInteger Vl = BigInteger.Two; + BigInteger Vh = P; + BigInteger Ql = BigInteger.One; + BigInteger Qh = BigInteger.One; + + for (int j = n - 1; j >= s + 1; --j) + { + Ql = Ql.Multiply(Qh).Mod(p); + + if (k.TestBit(j)) + { + Qh = Ql.Multiply(Q).Mod(p); + Uh = Uh.Multiply(Vh).Mod(p); + Vl = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p); + Vh = Vh.Multiply(Vh).Subtract(Qh.ShiftLeft(1)).Mod(p); + } + else + { + Qh = Ql; + Uh = Uh.Multiply(Vl).Subtract(Ql).Mod(p); + Vh = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p); + Vl = Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)).Mod(p); + } + } + + Ql = Ql.Multiply(Qh).Mod(p); + Qh = Ql.Multiply(Q).Mod(p); + Uh = Uh.Multiply(Vl).Subtract(Ql).Mod(p); + Vl = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p); + Ql = Ql.Multiply(Qh).Mod(p); + + for (int j = 1; j <= s; ++j) + { + Uh = Uh.Multiply(Vl).Mod(p); + Vl = Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)).Mod(p); + Ql = Ql.Multiply(Ql).Mod(p); + } + + return new BigInteger[]{ Uh, Vl }; + } + +// private static BigInteger[] verifyLucasSequence( +// BigInteger p, +// BigInteger P, +// BigInteger Q, +// BigInteger k) +// { +// BigInteger[] actual = fastLucasSequence(p, P, Q, k); +// BigInteger[] plus1 = fastLucasSequence(p, P, Q, k.Add(BigInteger.One)); +// BigInteger[] plus2 = fastLucasSequence(p, P, Q, k.Add(BigInteger.Two)); +// +// BigInteger[] check = stepLucasSequence(p, P, Q, actual, plus1); +// +// Debug.Assert(check[0].Equals(plus2[0])); +// Debug.Assert(check[1].Equals(plus2[1])); +// +// return actual; +// } +// +// private static BigInteger[] stepLucasSequence( +// BigInteger p, +// BigInteger P, +// BigInteger Q, +// BigInteger[] backTwo, +// BigInteger[] backOne) +// { +// return new BigInteger[] +// { +// P.Multiply(backOne[0]).Subtract(Q.Multiply(backTwo[0])).Mod(p), +// P.Multiply(backOne[1]).Subtract(Q.Multiply(backTwo[1])).Mod(p) +// }; +// } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + FpFieldElement other = obj as FpFieldElement; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + FpFieldElement other) + { + return q.Equals(other.q) && base.Equals(other); + } + + public override int GetHashCode() + { + return q.GetHashCode() ^ base.GetHashCode(); + } + } + +// /** +// * Class representing the Elements of the finite field +// * F2m in polynomial basis (PB) +// * representation. Both trinomial (Tpb) and pentanomial (Ppb) polynomial +// * basis representations are supported. Gaussian normal basis (GNB) +// * representation is not supported. +// */ +// public class F2mFieldElement +// : ECFieldElement +// { +// /** +// * Indicates gaussian normal basis representation (GNB). Number chosen +// * according to X9.62. GNB is not implemented at present. +// */ +// public const int Gnb = 1; +// +// /** +// * Indicates trinomial basis representation (Tpb). Number chosen +// * according to X9.62. +// */ +// public const int Tpb = 2; +// +// /** +// * Indicates pentanomial basis representation (Ppb). Number chosen +// * according to X9.62. +// */ +// public const int Ppb = 3; +// +// /** +// * Tpb or Ppb. +// */ +// private int representation; +// +// /** +// * The exponent m of F2m. +// */ +// private int m; +// +// /** +// * Tpb: The integer k where xm + +// * xk + 1 represents the reduction polynomial +// * f(z).
    +// * Ppb: The integer k1 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z).
    +// */ +// private int k1; +// +// /** +// * Tpb: Always set to 0
    +// * Ppb: The integer k2 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z).
    +// */ +// private int k2; +// +// /** +// * Tpb: Always set to 0
    +// * Ppb: The integer k3 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z).
    +// */ +// private int k3; +// +// /** +// * Constructor for Ppb. +// * @param m The exponent m of +// * F2m. +// * @param k1 The integer k1 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z). +// * @param k2 The integer k2 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z). +// * @param k3 The integer k3 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z). +// * @param x The BigInteger representing the value of the field element. +// */ +// public F2mFieldElement( +// int m, +// int k1, +// int k2, +// int k3, +// BigInteger x) +// : base(x) +// { +// if ((k2 == 0) && (k3 == 0)) +// { +// this.representation = Tpb; +// } +// else +// { +// if (k2 >= k3) +// throw new ArgumentException("k2 must be smaller than k3"); +// if (k2 <= 0) +// throw new ArgumentException("k2 must be larger than 0"); +// +// this.representation = Ppb; +// } +// +// if (x.SignValue < 0) +// throw new ArgumentException("x value cannot be negative"); +// +// this.m = m; +// this.k1 = k1; +// this.k2 = k2; +// this.k3 = k3; +// } +// +// /** +// * Constructor for Tpb. +// * @param m The exponent m of +// * F2m. +// * @param k The integer k where xm + +// * xk + 1 represents the reduction +// * polynomial f(z). +// * @param x The BigInteger representing the value of the field element. +// */ +// public F2mFieldElement( +// int m, +// int k, +// BigInteger x) +// : this(m, k, 0, 0, x) +// { +// // Set k1 to k, and set k2 and k3 to 0 +// } +// +// public override string FieldName +// { +// get { return "F2m"; } +// } +// +// /** +// * Checks, if the ECFieldElements a and b +// * are elements of the same field F2m +// * (having the same representation). +// * @param a field element. +// * @param b field element to be compared. +// * @throws ArgumentException if a and b +// * are not elements of the same field +// * F2m (having the same +// * representation). +// */ +// public static void CheckFieldElements( +// ECFieldElement a, +// ECFieldElement b) +// { +// if (!(a is F2mFieldElement) || !(b is F2mFieldElement)) +// { +// throw new ArgumentException("Field elements are not " +// + "both instances of F2mFieldElement"); +// } +// +// if ((a.x.SignValue < 0) || (b.x.SignValue < 0)) +// { +// throw new ArgumentException( +// "x value may not be negative"); +// } +// +// F2mFieldElement aF2m = (F2mFieldElement)a; +// F2mFieldElement bF2m = (F2mFieldElement)b; +// +// if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1) +// || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3)) +// { +// throw new ArgumentException("Field elements are not " +// + "elements of the same field F2m"); +// } +// +// if (aF2m.representation != bF2m.representation) +// { +// // Should never occur +// throw new ArgumentException( +// "One of the field " +// + "elements are not elements has incorrect representation"); +// } +// } +// +// /** +// * Computes z * a(z) mod f(z), where f(z) is +// * the reduction polynomial of this. +// * @param a The polynomial a(z) to be multiplied by +// * z mod f(z). +// * @return z * a(z) mod f(z) +// */ +// private BigInteger multZModF( +// BigInteger a) +// { +// // Left-shift of a(z) +// BigInteger az = a.ShiftLeft(1); +// if (az.TestBit(this.m)) +// { +// // If the coefficient of z^m in a(z) Equals 1, reduction +// // modulo f(z) is performed: Add f(z) to to a(z): +// // Step 1: Unset mth coeffient of a(z) +// az = az.ClearBit(this.m); +// +// // Step 2: Add r(z) to a(z), where r(z) is defined as +// // f(z) = z^m + r(z), and k1, k2, k3 are the positions of +// // the non-zero coefficients in r(z) +// az = az.FlipBit(0); +// az = az.FlipBit(this.k1); +// if (this.representation == Ppb) +// { +// az = az.FlipBit(this.k2); +// az = az.FlipBit(this.k3); +// } +// } +// return az; +// } +// +// public override ECFieldElement Add( +// ECFieldElement b) +// { +// // No check performed here for performance reasons. Instead the +// // elements involved are checked in ECPoint.F2m +// // checkFieldElements(this, b); +// if (b.x.SignValue == 0) +// return this; +// +// return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, this.x.Xor(b.x)); +// } +// +// public override ECFieldElement Subtract( +// ECFieldElement b) +// { +// // Addition and subtraction are the same in F2m +// return Add(b); +// } +// +// public override ECFieldElement Multiply( +// ECFieldElement b) +// { +// // Left-to-right shift-and-add field multiplication in F2m +// // Input: Binary polynomials a(z) and b(z) of degree at most m-1 +// // Output: c(z) = a(z) * b(z) mod f(z) +// +// // No check performed here for performance reasons. Instead the +// // elements involved are checked in ECPoint.F2m +// // checkFieldElements(this, b); +// BigInteger az = this.x; +// BigInteger bz = b.x; +// BigInteger cz; +// +// // Compute c(z) = a(z) * b(z) mod f(z) +// if (az.TestBit(0)) +// { +// cz = bz; +// } +// else +// { +// cz = BigInteger.Zero; +// } +// +// for (int i = 1; i < this.m; i++) +// { +// // b(z) := z * b(z) mod f(z) +// bz = multZModF(bz); +// +// if (az.TestBit(i)) +// { +// // If the coefficient of x^i in a(z) Equals 1, b(z) is added +// // to c(z) +// cz = cz.Xor(bz); +// } +// } +// return new F2mFieldElement(m, this.k1, this.k2, this.k3, cz); +// } +// +// +// public override ECFieldElement Divide( +// ECFieldElement b) +// { +// // There may be more efficient implementations +// ECFieldElement bInv = b.Invert(); +// return Multiply(bInv); +// } +// +// public override ECFieldElement Negate() +// { +// // -x == x holds for all x in F2m +// return this; +// } +// +// public override ECFieldElement Square() +// { +// // Naive implementation, can probably be speeded up using modular +// // reduction +// return Multiply(this); +// } +// +// public override ECFieldElement Invert() +// { +// // Inversion in F2m using the extended Euclidean algorithm +// // Input: A nonzero polynomial a(z) of degree at most m-1 +// // Output: a(z)^(-1) mod f(z) +// +// // u(z) := a(z) +// BigInteger uz = this.x; +// if (uz.SignValue <= 0) +// { +// throw new ArithmeticException("x is zero or negative, " + +// "inversion is impossible"); +// } +// +// // v(z) := f(z) +// BigInteger vz = BigInteger.One.ShiftLeft(m); +// vz = vz.SetBit(0); +// vz = vz.SetBit(this.k1); +// if (this.representation == Ppb) +// { +// vz = vz.SetBit(this.k2); +// vz = vz.SetBit(this.k3); +// } +// +// // g1(z) := 1, g2(z) := 0 +// BigInteger g1z = BigInteger.One; +// BigInteger g2z = BigInteger.Zero; +// +// // while u != 1 +// while (uz.SignValue != 0) +// { +// // j := deg(u(z)) - deg(v(z)) +// int j = uz.BitLength - vz.BitLength; +// +// // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j +// if (j < 0) +// { +// BigInteger uzCopy = uz; +// uz = vz; +// vz = uzCopy; +// +// BigInteger g1zCopy = g1z; +// g1z = g2z; +// g2z = g1zCopy; +// +// j = -j; +// } +// +// // u(z) := u(z) + z^j * v(z) +// // Note, that no reduction modulo f(z) is required, because +// // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z))) +// // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z)) +// // = deg(u(z)) +// uz = uz.Xor(vz.ShiftLeft(j)); +// +// // g1(z) := g1(z) + z^j * g2(z) +// g1z = g1z.Xor(g2z.ShiftLeft(j)); +// // if (g1z.BitLength() > this.m) { +// // throw new ArithmeticException( +// // "deg(g1z) >= m, g1z = " + g1z.ToString(2)); +// // } +// } +// return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, g2z); +// } +// +// public override ECFieldElement Sqrt() +// { +// throw new ArithmeticException("Not implemented"); +// } +// +// /** +// * @return the representation of the field +// * F2m, either of +// * {@link F2mFieldElement.Tpb} (trinomial +// * basis representation) or +// * {@link F2mFieldElement.Ppb} (pentanomial +// * basis representation). +// */ +// public int Representation +// { +// get { return this.representation; } +// } +// +// /** +// * @return the degree m of the reduction polynomial +// * f(z). +// */ +// public int M +// { +// get { return this.m; } +// } +// +// /** +// * @return Tpb: The integer k where xm + +// * xk + 1 represents the reduction polynomial +// * f(z).
    +// * Ppb: The integer k1 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z).
    +// */ +// public int K1 +// { +// get { return this.k1; } +// } +// +// /** +// * @return Tpb: Always returns 0
    +// * Ppb: The integer k2 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z).
    +// */ +// public int K2 +// { +// get { return this.k2; } +// } +// +// /** +// * @return Tpb: Always set to 0
    +// * Ppb: The integer k3 where xm + +// * xk3 + xk2 + xk1 + 1 +// * represents the reduction polynomial f(z).
    +// */ +// public int K3 +// { +// get { return this.k3; } +// } +// +// public override bool Equals( +// object obj) +// { +// if (obj == this) +// return true; +// +// F2mFieldElement other = obj as F2mFieldElement; +// +// if (other == null) +// return false; +// +// return Equals(other); +// } +// +// protected bool Equals( +// F2mFieldElement other) +// { +// return m == other.m +// && k1 == other.k1 +// && k2 == other.k2 +// && k3 == other.k3 +// && representation == other.representation +// && base.Equals(other); +// } +// +// public override int GetHashCode() +// { +// return base.GetHashCode() ^ m ^ k1 ^ k2 ^ k3; +// } +// } + + /** + * Class representing the Elements of the finite field + * F2m in polynomial basis (PB) + * representation. Both trinomial (Tpb) and pentanomial (Ppb) polynomial + * basis representations are supported. Gaussian normal basis (GNB) + * representation is not supported. + */ + public class F2mFieldElement + : ECFieldElement + { + /** + * Indicates gaussian normal basis representation (GNB). Number chosen + * according to X9.62. GNB is not implemented at present. + */ + public const int Gnb = 1; + + /** + * Indicates trinomial basis representation (Tpb). Number chosen + * according to X9.62. + */ + public const int Tpb = 2; + + /** + * Indicates pentanomial basis representation (Ppb). Number chosen + * according to X9.62. + */ + public const int Ppb = 3; + + /** + * Tpb or Ppb. + */ + private int representation; + + /** + * The exponent m of F2m. + */ + private int m; + + /** + * Tpb: The integer k where xm + + * xk + 1 represents the reduction polynomial + * f(z).
    + * Ppb: The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + private int k1; + + /** + * Tpb: Always set to 0
    + * Ppb: The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + private int k2; + + /** + * Tpb: Always set to 0
    + * Ppb: The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + private int k3; + + /** + * The IntArray holding the bits. + */ + private IntArray x; + + /** + * The number of ints required to hold m bits. + */ + private readonly int t; + + /** + * Constructor for Ppb. + * @param m The exponent m of + * F2m. + * @param k1 The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k2 The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k3 The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param x The BigInteger representing the value of the field element. + */ + public F2mFieldElement( + int m, + int k1, + int k2, + int k3, + BigInteger x) + { + // t = m / 32 rounded up to the next integer + this.t = (m + 31) >> 5; + this.x = new IntArray(x, t); + + if ((k2 == 0) && (k3 == 0)) + { + this.representation = Tpb; + } + else + { + if (k2 >= k3) + throw new ArgumentException("k2 must be smaller than k3"); + if (k2 <= 0) + throw new ArgumentException("k2 must be larger than 0"); + + this.representation = Ppb; + } + + if (x.SignValue < 0) + throw new ArgumentException("x value cannot be negative"); + + this.m = m; + this.k1 = k1; + this.k2 = k2; + this.k3 = k3; + } + + /** + * Constructor for Tpb. + * @param m The exponent m of + * F2m. + * @param k The integer k where xm + + * xk + 1 represents the reduction + * polynomial f(z). + * @param x The BigInteger representing the value of the field element. + */ + public F2mFieldElement( + int m, + int k, + BigInteger x) + : this(m, k, 0, 0, x) + { + // Set k1 to k, and set k2 and k3 to 0 + } + + private F2mFieldElement(int m, int k1, int k2, int k3, IntArray x) + { + t = (m + 31) >> 5; + this.x = x; + this.m = m; + this.k1 = k1; + this.k2 = k2; + this.k3 = k3; + + if ((k2 == 0) && (k3 == 0)) + { + this.representation = Tpb; + } + else + { + this.representation = Ppb; + } + } + + public override BigInteger ToBigInteger() + { + return x.ToBigInteger(); + } + + public override string FieldName + { + get { return "F2m"; } + } + + public override int FieldSize + { + get { return m; } + } + + /** + * Checks, if the ECFieldElements a and b + * are elements of the same field F2m + * (having the same representation). + * @param a field element. + * @param b field element to be compared. + * @throws ArgumentException if a and b + * are not elements of the same field + * F2m (having the same + * representation). + */ + public static void CheckFieldElements( + ECFieldElement a, + ECFieldElement b) + { + if (!(a is F2mFieldElement) || !(b is F2mFieldElement)) + { + throw new ArgumentException("Field elements are not " + + "both instances of F2mFieldElement"); + } + + F2mFieldElement aF2m = (F2mFieldElement)a; + F2mFieldElement bF2m = (F2mFieldElement)b; + + if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1) + || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3)) + { + throw new ArgumentException("Field elements are not " + + "elements of the same field F2m"); + } + + if (aF2m.representation != bF2m.representation) + { + // Should never occur + throw new ArgumentException( + "One of the field " + + "elements are not elements has incorrect representation"); + } + } + + public override ECFieldElement Add( + ECFieldElement b) + { + // No check performed here for performance reasons. Instead the + // elements involved are checked in ECPoint.F2m + // checkFieldElements(this, b); + IntArray iarrClone = (IntArray) this.x.Clone(); + F2mFieldElement bF2m = (F2mFieldElement) b; + iarrClone.AddShifted(bF2m.x, 0); + return new F2mFieldElement(m, k1, k2, k3, iarrClone); + } + + public override ECFieldElement Subtract( + ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply( + ECFieldElement b) + { + // Right-to-left comb multiplication in the IntArray + // Input: Binary polynomials a(z) and b(z) of degree at most m-1 + // Output: c(z) = a(z) * b(z) mod f(z) + + // No check performed here for performance reasons. Instead the + // elements involved are checked in ECPoint.F2m + // checkFieldElements(this, b); + F2mFieldElement bF2m = (F2mFieldElement) b; + IntArray mult = x.Multiply(bF2m.x, m); + mult.Reduce(m, new int[]{k1, k2, k3}); + return new F2mFieldElement(m, k1, k2, k3, mult); + } + + public override ECFieldElement Divide( + ECFieldElement b) + { + // There may be more efficient implementations + ECFieldElement bInv = b.Invert(); + return Multiply(bInv); + } + + public override ECFieldElement Negate() + { + // -x == x holds for all x in F2m + return this; + } + + public override ECFieldElement Square() + { + IntArray squared = x.Square(m); + squared.Reduce(m, new int[]{k1, k2, k3}); + return new F2mFieldElement(m, k1, k2, k3, squared); + } + + public override ECFieldElement Invert() + { + // Inversion in F2m using the extended Euclidean algorithm + // Input: A nonzero polynomial a(z) of degree at most m-1 + // Output: a(z)^(-1) mod f(z) + + // u(z) := a(z) + IntArray uz = (IntArray)this.x.Clone(); + + // v(z) := f(z) + IntArray vz = new IntArray(t); + vz.SetBit(m); + vz.SetBit(0); + vz.SetBit(this.k1); + if (this.representation == Ppb) + { + vz.SetBit(this.k2); + vz.SetBit(this.k3); + } + + // g1(z) := 1, g2(z) := 0 + IntArray g1z = new IntArray(t); + g1z.SetBit(0); + IntArray g2z = new IntArray(t); + + // while u != 0 + while (uz.GetUsedLength() > 0) +// while (uz.bitLength() > 1) + { + // j := deg(u(z)) - deg(v(z)) + int j = uz.BitLength - vz.BitLength; + + // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j + if (j < 0) + { + IntArray uzCopy = uz; + uz = vz; + vz = uzCopy; + + IntArray g1zCopy = g1z; + g1z = g2z; + g2z = g1zCopy; + + j = -j; + } + + // u(z) := u(z) + z^j * v(z) + // Note, that no reduction modulo f(z) is required, because + // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z))) + // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z)) + // = deg(u(z)) + // uz = uz.xor(vz.ShiftLeft(j)); + // jInt = n / 32 + int jInt = j >> 5; + // jInt = n % 32 + int jBit = j & 0x1F; + IntArray vzShift = vz.ShiftLeft(jBit); + uz.AddShifted(vzShift, jInt); + + // g1(z) := g1(z) + z^j * g2(z) +// g1z = g1z.xor(g2z.ShiftLeft(j)); + IntArray g2zShift = g2z.ShiftLeft(jBit); + g1z.AddShifted(g2zShift, jInt); + } + return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, g2z); + } + + public override ECFieldElement Sqrt() + { + throw new ArithmeticException("Not implemented"); + } + + /** + * @return the representation of the field + * F2m, either of + * {@link F2mFieldElement.Tpb} (trinomial + * basis representation) or + * {@link F2mFieldElement.Ppb} (pentanomial + * basis representation). + */ + public int Representation + { + get { return this.representation; } + } + + /** + * @return the degree m of the reduction polynomial + * f(z). + */ + public int M + { + get { return this.m; } + } + + /** + * @return Tpb: The integer k where xm + + * xk + 1 represents the reduction polynomial + * f(z).
    + * Ppb: The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + public int K1 + { + get { return this.k1; } + } + + /** + * @return Tpb: Always returns 0
    + * Ppb: The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + public int K2 + { + get { return this.k2; } + } + + /** + * @return Tpb: Always set to 0
    + * Ppb: The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + public int K3 + { + get { return this.k3; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + F2mFieldElement other = obj as F2mFieldElement; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + F2mFieldElement other) + { + return m == other.m + && k1 == other.k1 + && k2 == other.k2 + && k3 == other.k3 + && representation == other.representation + && base.Equals(other); + } + + public override int GetHashCode() + { + return m.GetHashCode() + ^ k1.GetHashCode() + ^ k2.GetHashCode() + ^ k3.GetHashCode() + ^ representation.GetHashCode() + ^ base.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/math/ec/ECPoint.cs b/src/core/srcbc/math/ec/ECPoint.cs new file mode 100644 index 0000000..4792e70 --- /dev/null +++ b/src/core/srcbc/math/ec/ECPoint.cs @@ -0,0 +1,566 @@ +using System; +using System.Collections; +using System.Diagnostics; + +using Org.BouncyCastle.Asn1.X9; + +using Org.BouncyCastle.Math.EC.Multiplier; + +namespace Org.BouncyCastle.Math.EC +{ + /** + * base class for points on elliptic curves. + */ + public abstract class ECPoint + { + internal readonly ECCurve curve; + internal readonly ECFieldElement x, y; + internal readonly bool withCompression; + internal ECMultiplier multiplier = null; + internal PreCompInfo preCompInfo = null; + + protected internal ECPoint( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + { + if (curve == null) + throw new ArgumentNullException("curve"); + + this.curve = curve; + this.x = x; + this.y = y; + this.withCompression = withCompression; + } + + public ECCurve Curve + { + get { return curve; } + } + + public ECFieldElement X + { + get { return x; } + } + + public ECFieldElement Y + { + get { return y; } + } + + public bool IsInfinity + { + get { return x == null && y == null; } + } + + public bool IsCompressed + { + get { return withCompression; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECPoint o = obj as ECPoint; + + if (o == null) + return false; + + if (this.IsInfinity) + return o.IsInfinity; + + return x.Equals(o.x) && y.Equals(o.y); + } + + public override int GetHashCode() + { + if (this.IsInfinity) + return 0; + + return x.GetHashCode() ^ y.GetHashCode(); + } + +// /** +// * Mainly for testing. Explicitly set the ECMultiplier. +// * @param multiplier The ECMultiplier to be used to multiply +// * this ECPoint. +// */ +// internal void SetECMultiplier( +// ECMultiplier multiplier) +// { +// this.multiplier = multiplier; +// } + + /** + * Sets the PreCompInfo. Used by ECMultipliers + * to save the precomputation for this ECPoint to store the + * precomputation result for use by subsequent multiplication. + * @param preCompInfo The values precomputed by the + * ECMultiplier. + */ + internal void SetPreCompInfo( + PreCompInfo preCompInfo) + { + this.preCompInfo = preCompInfo; + } + + public abstract byte[] GetEncoded(); + + public abstract ECPoint Add(ECPoint b); + public abstract ECPoint Subtract(ECPoint b); + public abstract ECPoint Negate(); + public abstract ECPoint Twice(); + public abstract ECPoint Multiply(BigInteger b); + + /** + * Sets the appropriate ECMultiplier, unless already set. + */ + internal virtual void AssertECMultiplier() + { + if (this.multiplier == null) + { + lock (this) + { + if (this.multiplier == null) + { + this.multiplier = new FpNafMultiplier(); + } + } + } + } + } + + public abstract class ECPointBase + : ECPoint + { + protected internal ECPointBase( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + : base(curve, x, y, withCompression) + { + } + + protected internal abstract bool YTilde { get; } + + /** + * return the field element encoded with point compression. (S 4.3.6) + */ + public override byte[] GetEncoded() + { + if (this.IsInfinity) + return new byte[1]; + + // Note: some of the tests rely on calculating byte length from the field element + // (since the test cases use mismatching fields for curve/elements) + int byteLength = X9IntegerConverter.GetByteLength(x); + byte[] X = X9IntegerConverter.IntegerToBytes(this.X.ToBigInteger(), byteLength); + byte[] PO; + + if (withCompression) + { + PO = new byte[1 + X.Length]; + + PO[0] = (byte)(YTilde ? 0x03 : 0x02); + } + else + { + byte[] Y = X9IntegerConverter.IntegerToBytes(this.Y.ToBigInteger(), byteLength); + PO = new byte[1 + X.Length + Y.Length]; + + PO[0] = 0x04; + + Y.CopyTo(PO, 1 + X.Length); + } + + X.CopyTo(PO, 1); + + return PO; + } + + /** + * Multiplies this ECPoint by the given number. + * @param k The multiplicator. + * @return k * this. + */ + public override ECPoint Multiply( + BigInteger k) + { + if (this.IsInfinity) + return this; + + if (k.SignValue == 0) + return this.curve.Infinity; + + AssertECMultiplier(); + return this.multiplier.Multiply(this, k, preCompInfo); + } + } + + /** + * Elliptic curve points over Fp + */ + public class FpPoint + : ECPointBase + { + /** + * Create a point which encodes with point compression. + * + * @param curve the curve to use + * @param x affine x co-ordinate + * @param y affine y co-ordinate + */ + public FpPoint( + ECCurve curve, + ECFieldElement x, + ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * Create a point that encodes with or without point compresion. + * + * @param curve the curve to use + * @param x affine x co-ordinate + * @param y affine y co-ordinate + * @param withCompression if true encode with point compression + */ + public FpPoint( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x != null && y == null) || (x == null && y != null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + protected internal override bool YTilde + { + get + { + return this.Y.ToBigInteger().TestBit(0); + } + } + + // B.3 pg 62 + public override ECPoint Add( + ECPoint b) + { + if (this.IsInfinity) + return b; + + if (b.IsInfinity) + return this; + + // Check if b = this or b = -this + if (this.x.Equals(b.x)) + { + if (this.y.Equals(b.y)) + { + // this = b, i.e. this must be doubled + return this.Twice(); + } + + Debug.Assert(this.y.Equals(b.y.Negate())); + + // this = -b, i.e. the result is the point at infinity + return this.curve.Infinity; + } + + ECFieldElement gamma = b.y.Subtract(this.y).Divide(b.x.Subtract(this.x)); + + ECFieldElement x3 = gamma.Square().Subtract(this.x).Subtract(b.x); + ECFieldElement y3 = gamma.Multiply(this.x.Subtract(x3)).Subtract(this.y); + + return new FpPoint(curve, x3, y3); + } + + // B.3 pg 62 + public override ECPoint Twice() + { + // Twice identity element (point at infinity) is identity + if (this.IsInfinity) + return this; + + // if y1 == 0, then (x1, y1) == (x1, -y1) + // and hence this = -this and thus 2(x1, y1) == infinity + if (this.y.ToBigInteger().SignValue == 0) + return this.curve.Infinity; + + ECFieldElement TWO = this.curve.FromBigInteger(BigInteger.Two); + ECFieldElement THREE = this.curve.FromBigInteger(BigInteger.Three); + ECFieldElement gamma = this.x.Square().Multiply(THREE).Add(curve.a).Divide(y.Multiply(TWO)); + + ECFieldElement x3 = gamma.Square().Subtract(this.x.Multiply(TWO)); + ECFieldElement y3 = gamma.Multiply(this.x.Subtract(x3)).Subtract(this.y); + + return new FpPoint(curve, x3, y3, this.withCompression); + } + + // D.3.2 pg 102 (see Note:) + public override ECPoint Subtract( + ECPoint b) + { + if (b.IsInfinity) + return this; + + // Add -b + return Add(b.Negate()); + } + + public override ECPoint Negate() + { + return new FpPoint(this.curve, this.x, this.y.Negate(), this.withCompression); + } + + // TODO Uncomment this to enable WNAF Fp point multiplication +// /** +// * Sets the default ECMultiplier, unless already set. +// */ +// internal override void AssertECMultiplier() +// { +// if (this.multiplier == null) +// { +// lock (this) +// { +// if (this.multiplier == null) +// { +// this.multiplier = new WNafMultiplier(); +// } +// } +// } +// } + } + + /** + * Elliptic curve points over F2m + */ + public class F2mPoint + : ECPointBase + { + /** + * @param curve base curve + * @param x x point + * @param y y point + */ + public F2mPoint( + ECCurve curve, + ECFieldElement x, + ECFieldElement y) + : this(curve, x, y, false) + { + } + + /** + * @param curve base curve + * @param x x point + * @param y y point + * @param withCompression true if encode with point compression. + */ + public F2mPoint( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x != null && y == null) || (x == null && y != null)) + { + throw new ArgumentException("Exactly one of the field elements is null"); + } + + if (x != null) + { + // Check if x and y are elements of the same field + F2mFieldElement.CheckFieldElements(this.x, this.y); + + // Check if x and a are elements of the same field + F2mFieldElement.CheckFieldElements(this.x, this.curve.A); + } + } + + /** + * Constructor for point at infinity + */ + [Obsolete("Use ECCurve.Infinity property")] + public F2mPoint( + ECCurve curve) + : this(curve, null, null) + { + } + + protected internal override bool YTilde + { + get + { + // X9.62 4.2.2 and 4.3.6: + // if x = 0 then ypTilde := 0, else ypTilde is the rightmost + // bit of y * x^(-1) + return this.X.ToBigInteger().SignValue != 0 + && this.Y.Multiply(this.X.Invert()).ToBigInteger().TestBit(0); + } + } + + /** + * Check, if two ECPoints can be added or subtracted. + * @param a The first ECPoint to check. + * @param b The second ECPoint to check. + * @throws IllegalArgumentException if a and b + * cannot be added. + */ + private static void CheckPoints( + ECPoint a, + ECPoint b) + { + // Check, if points are on the same curve + if (!a.curve.Equals(b.curve)) + throw new ArgumentException("Only points on the same curve can be added or subtracted"); + +// F2mFieldElement.CheckFieldElements(a.x, b.x); + } + + /* (non-Javadoc) + * @see org.bouncycastle.math.ec.ECPoint#add(org.bouncycastle.math.ec.ECPoint) + */ + public override ECPoint Add(ECPoint b) + { + CheckPoints(this, b); + return AddSimple((F2mPoint) b); + } + + /** + * Adds another ECPoints.F2m to this without + * checking if both points are on the same curve. Used by multiplication + * algorithms, because there all points are a multiple of the same point + * and hence the checks can be omitted. + * @param b The other ECPoints.F2m to add to + * this. + * @return this + b + */ + internal F2mPoint AddSimple(F2mPoint b) + { + if (this.IsInfinity) + return b; + + if (b.IsInfinity) + return this; + + F2mFieldElement x2 = (F2mFieldElement) b.X; + F2mFieldElement y2 = (F2mFieldElement) b.Y; + + // Check if b == this or b == -this + if (this.x.Equals(x2)) + { + // this == b, i.e. this must be doubled + if (this.y.Equals(y2)) + return (F2mPoint) this.Twice(); + + // this = -other, i.e. the result is the point at infinity + return (F2mPoint) this.curve.Infinity; + } + + ECFieldElement xSum = this.x.Add(x2); + + F2mFieldElement lambda + = (F2mFieldElement)(this.y.Add(y2)).Divide(xSum); + + F2mFieldElement x3 + = (F2mFieldElement)lambda.Square().Add(lambda).Add(xSum).Add(this.curve.A); + + F2mFieldElement y3 + = (F2mFieldElement)lambda.Multiply(this.x.Add(x3)).Add(x3).Add(this.y); + + return new F2mPoint(curve, x3, y3, withCompression); + } + + /* (non-Javadoc) + * @see org.bouncycastle.math.ec.ECPoint#subtract(org.bouncycastle.math.ec.ECPoint) + */ + public override ECPoint Subtract( + ECPoint b) + { + CheckPoints(this, b); + return SubtractSimple((F2mPoint) b); + } + + /** + * Subtracts another ECPoints.F2m from this + * without checking if both points are on the same curve. Used by + * multiplication algorithms, because there all points are a multiple + * of the same point and hence the checks can be omitted. + * @param b The other ECPoints.F2m to subtract from + * this. + * @return this - b + */ + internal F2mPoint SubtractSimple( + F2mPoint b) + { + if (b.IsInfinity) + return this; + + // Add -b + return AddSimple((F2mPoint) b.Negate()); + } + + /* (non-Javadoc) + * @see Org.BouncyCastle.Math.EC.ECPoint#twice() + */ + public override ECPoint Twice() + { + // Twice identity element (point at infinity) is identity + if (this.IsInfinity) + return this; + + // if x1 == 0, then (x1, y1) == (x1, x1 + y1) + // and hence this = -this and thus 2(x1, y1) == infinity + if (this.x.ToBigInteger().SignValue == 0) + return this.curve.Infinity; + + F2mFieldElement lambda = (F2mFieldElement) this.x.Add(this.y.Divide(this.x)); + F2mFieldElement x2 = (F2mFieldElement)lambda.Square().Add(lambda).Add(this.curve.A); + ECFieldElement ONE = this.curve.FromBigInteger(BigInteger.One); + F2mFieldElement y2 = (F2mFieldElement)this.x.Square().Add( + x2.Multiply(lambda.Add(ONE))); + + return new F2mPoint(this.curve, x2, y2, withCompression); + } + + public override ECPoint Negate() + { + return new F2mPoint(curve, this.x, this.x.Add(this.y), withCompression); + } + + // TODO Uncomment this to enable WNAF/WTNAF F2m point multiplication +// /** +// * Sets the appropriate ECMultiplier, unless already set. +// */ +// internal override void AssertECMultiplier() +// { +// if (this.multiplier == null) +// { +// lock (this) +// { +// if (this.multiplier == null) +// { +// if (((F2mCurve) this.curve).IsKoblitz) +// { +// this.multiplier = new WTauNafMultiplier(); +// } +// else +// { +// this.multiplier = new WNafMultiplier(); +// } +// } +// } +// } +// } + } +} diff --git a/src/core/srcbc/math/ec/IntArray.cs b/src/core/srcbc/math/ec/IntArray.cs new file mode 100644 index 0000000..d16197d --- /dev/null +++ b/src/core/srcbc/math/ec/IntArray.cs @@ -0,0 +1,486 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Math.EC +{ + internal class IntArray + : ICloneable + { + // TODO make m fixed for the IntArray, and hence compute T once and for all + + // TODO Use uint's internally? + private int[] m_ints; + + public IntArray(int intLen) + { + m_ints = new int[intLen]; + } + + private IntArray(int[] ints) + { + m_ints = ints; + } + + public IntArray(BigInteger bigInt) + : this(bigInt, 0) + { + } + + public IntArray(BigInteger bigInt, int minIntLen) + { + if (bigInt.SignValue == -1) + throw new ArgumentException("Only positive Integers allowed", "bigint"); + + if (bigInt.SignValue == 0) + { + m_ints = new int[] { 0 }; + return; + } + + byte[] barr = bigInt.ToByteArrayUnsigned(); + int barrLen = barr.Length; + + int intLen = (barrLen + 3) / 4; + m_ints = new int[System.Math.Max(intLen, minIntLen)]; + + int rem = barrLen % 4; + int barrI = 0; + + if (0 < rem) + { + int temp = (int) barr[barrI++]; + while (barrI < rem) + { + temp = temp << 8 | (int) barr[barrI++]; + } + m_ints[--intLen] = temp; + } + + while (intLen > 0) + { + int temp = (int) barr[barrI++]; + for (int i = 1; i < 4; i++) + { + temp = temp << 8 | (int) barr[barrI++]; + } + m_ints[--intLen] = temp; + } + } + + public int GetUsedLength() + { + int highestIntPos = m_ints.Length; + + if (highestIntPos < 1) + return 0; + + // Check if first element will act as sentinel + if (m_ints[0] != 0) + { + while (m_ints[--highestIntPos] == 0) + { + } + return highestIntPos + 1; + } + + do + { + if (m_ints[--highestIntPos] != 0) + { + return highestIntPos + 1; + } + } + while (highestIntPos > 0); + + return 0; + } + + public int BitLength + { + get + { + // JDK 1.5: see Integer.numberOfLeadingZeros() + int intLen = GetUsedLength(); + if (intLen == 0) + return 0; + + int last = intLen - 1; + uint highest = (uint) m_ints[last]; + int bits = (last << 5) + 1; + + // A couple of binary search steps + if (highest > 0x0000ffff) + { + if (highest > 0x00ffffff) + { + bits += 24; + highest >>= 24; + } + else + { + bits += 16; + highest >>= 16; + } + } + else if (highest > 0x000000ff) + { + bits += 8; + highest >>= 8; + } + + while (highest > 1) + { + ++bits; + highest >>= 1; + } + + return bits; + } + } + + private int[] resizedInts(int newLen) + { + int[] newInts = new int[newLen]; + int oldLen = m_ints.Length; + int copyLen = oldLen < newLen ? oldLen : newLen; + Array.Copy(m_ints, 0, newInts, 0, copyLen); + return newInts; + } + + public BigInteger ToBigInteger() + { + int usedLen = GetUsedLength(); + if (usedLen == 0) + { + return BigInteger.Zero; + } + + int highestInt = m_ints[usedLen - 1]; + byte[] temp = new byte[4]; + int barrI = 0; + bool trailingZeroBytesDone = false; + for (int j = 3; j >= 0; j--) + { + byte thisByte = (byte)((int)((uint) highestInt >> (8 * j))); + if (trailingZeroBytesDone || (thisByte != 0)) + { + trailingZeroBytesDone = true; + temp[barrI++] = thisByte; + } + } + + int barrLen = 4 * (usedLen - 1) + barrI; + byte[] barr = new byte[barrLen]; + for (int j = 0; j < barrI; j++) + { + barr[j] = temp[j]; + } + // Highest value int is done now + + for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--) + { + for (int j = 3; j >= 0; j--) + { + barr[barrI++] = (byte)((int)((uint)m_ints[iarrJ] >> (8 * j))); + } + } + return new BigInteger(1, barr); + } + + public void ShiftLeft() + { + int usedLen = GetUsedLength(); + if (usedLen == 0) + { + return; + } + if (m_ints[usedLen - 1] < 0) + { + // highest bit of highest used byte is set, so shifting left will + // make the IntArray one byte longer + usedLen++; + if (usedLen > m_ints.Length) + { + // make the m_ints one byte longer, because we need one more + // byte which is not available in m_ints + m_ints = resizedInts(m_ints.Length + 1); + } + } + + bool carry = false; + for (int i = 0; i < usedLen; i++) + { + // nextCarry is true if highest bit is set + bool nextCarry = m_ints[i] < 0; + m_ints[i] <<= 1; + if (carry) + { + // set lowest bit + m_ints[i] |= 1; + } + carry = nextCarry; + } + } + + public IntArray ShiftLeft(int n) + { + int usedLen = GetUsedLength(); + if (usedLen == 0) + { + return this; + } + + if (n == 0) + { + return this; + } + + if (n > 31) + { + throw new ArgumentException("shiftLeft() for max 31 bits " + + ", " + n + "bit shift is not possible", "n"); + } + + int[] newInts = new int[usedLen + 1]; + + int nm32 = 32 - n; + newInts[0] = m_ints[0] << n; + for (int i = 1; i < usedLen; i++) + { + newInts[i] = (m_ints[i] << n) | (int)((uint)m_ints[i - 1] >> nm32); + } + newInts[usedLen] = (int)((uint)m_ints[usedLen - 1] >> nm32); + + return new IntArray(newInts); + } + + public void AddShifted(IntArray other, int shift) + { + int usedLenOther = other.GetUsedLength(); + int newMinUsedLen = usedLenOther + shift; + if (newMinUsedLen > m_ints.Length) + { + m_ints = resizedInts(newMinUsedLen); + //Console.WriteLine("Resize required"); + } + + for (int i = 0; i < usedLenOther; i++) + { + m_ints[i + shift] ^= other.m_ints[i]; + } + } + + public int Length + { + get { return m_ints.Length; } + } + + public bool TestBit(int n) + { + // theInt = n / 32 + int theInt = n >> 5; + // theBit = n % 32 + int theBit = n & 0x1F; + int tester = 1 << theBit; + return ((m_ints[theInt] & tester) != 0); + } + + public void FlipBit(int n) + { + // theInt = n / 32 + int theInt = n >> 5; + // theBit = n % 32 + int theBit = n & 0x1F; + int flipper = 1 << theBit; + m_ints[theInt] ^= flipper; + } + + public void SetBit(int n) + { + // theInt = n / 32 + int theInt = n >> 5; + // theBit = n % 32 + int theBit = n & 0x1F; + int setter = 1 << theBit; + m_ints[theInt] |= setter; + } + + public IntArray Multiply(IntArray other, int m) + { + // Lenght of c is 2m bits rounded up to the next int (32 bit) + int t = (m + 31) >> 5; + if (m_ints.Length < t) + { + m_ints = resizedInts(t); + } + + IntArray b = new IntArray(other.resizedInts(other.Length + 1)); + IntArray c = new IntArray((m + m + 31) >> 5); + // IntArray c = new IntArray(t + t); + int testBit = 1; + for (int k = 0; k < 32; k++) + { + for (int j = 0; j < t; j++) + { + if ((m_ints[j] & testBit) != 0) + { + // The kth bit of m_ints[j] is set + c.AddShifted(b, j); + } + } + testBit <<= 1; + b.ShiftLeft(); + } + return c; + } + + // public IntArray multiplyLeftToRight(IntArray other, int m) { + // // Lenght of c is 2m bits rounded up to the next int (32 bit) + // int t = (m + 31) / 32; + // if (m_ints.Length < t) { + // m_ints = resizedInts(t); + // } + // + // IntArray b = new IntArray(other.resizedInts(other.getLength() + 1)); + // IntArray c = new IntArray((m + m + 31) / 32); + // // IntArray c = new IntArray(t + t); + // int testBit = 1 << 31; + // for (int k = 31; k >= 0; k--) { + // for (int j = 0; j < t; j++) { + // if ((m_ints[j] & testBit) != 0) { + // // The kth bit of m_ints[j] is set + // c.addShifted(b, j); + // } + // } + // testBit >>>= 1; + // if (k > 0) { + // c.shiftLeft(); + // } + // } + // return c; + // } + + // TODO note, redPol.Length must be 3 for TPB and 5 for PPB + public void Reduce(int m, int[] redPol) + { + for (int i = m + m - 2; i >= m; i--) + { + if (TestBit(i)) + { + int bit = i - m; + FlipBit(bit); + FlipBit(i); + int l = redPol.Length; + while (--l >= 0) + { + FlipBit(redPol[l] + bit); + } + } + } + m_ints = resizedInts((m + 31) >> 5); + } + + public IntArray Square(int m) + { + // TODO make the table static readonly + int[] table = { 0x0, 0x1, 0x4, 0x5, 0x10, 0x11, 0x14, 0x15, 0x40, + 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55 }; + + int t = (m + 31) >> 5; + if (m_ints.Length < t) + { + m_ints = resizedInts(t); + } + + IntArray c = new IntArray(t + t); + + // TODO twice the same code, put in separate private method + for (int i = 0; i < t; i++) + { + int v0 = 0; + for (int j = 0; j < 4; j++) + { + v0 = (int)((uint) v0 >> 8); + int u = (int)((uint)m_ints[i] >> (j * 4)) & 0xF; + int w = table[u] << 24; + v0 |= w; + } + c.m_ints[i + i] = v0; + + v0 = 0; + int upper = (int)((uint) m_ints[i] >> 16); + for (int j = 0; j < 4; j++) + { + v0 = (int)((uint) v0 >> 8); + int u = (int)((uint)upper >> (j * 4)) & 0xF; + int w = table[u] << 24; + v0 |= w; + } + c.m_ints[i + i + 1] = v0; + } + return c; + } + + public override bool Equals(object o) + { + if (!(o is IntArray)) + { + return false; + } + IntArray other = (IntArray) o; + int usedLen = GetUsedLength(); + if (other.GetUsedLength() != usedLen) + { + return false; + } + for (int i = 0; i < usedLen; i++) + { + if (m_ints[i] != other.m_ints[i]) + { + return false; + } + } + return true; + } + + public override int GetHashCode() + { + int i = GetUsedLength(); + int hc = i; + while (--i >= 0) + { + hc *= 17; + hc ^= m_ints[i]; + } + return hc; + } + + public object Clone() + { + return new IntArray((int[]) m_ints.Clone()); + } + + public override string ToString() + { + int usedLen = GetUsedLength(); + if (usedLen == 0) + { + return "0"; + } + + StringBuilder sb = new StringBuilder(Convert.ToString(m_ints[usedLen - 1], 2)); + for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--) + { + string hexString = Convert.ToString(m_ints[iarrJ], 2); + + // Add leading zeroes, except for highest significant int + for (int i = hexString.Length; i < 8; i++) + { + hexString = "0" + hexString; + } + sb.Append(hexString); + } + return sb.ToString(); + } + } +} diff --git a/src/core/srcbc/math/ec/abc/SimpleBigDecimal.cs b/src/core/srcbc/math/ec/abc/SimpleBigDecimal.cs new file mode 100644 index 0000000..47c165e --- /dev/null +++ b/src/core/srcbc/math/ec/abc/SimpleBigDecimal.cs @@ -0,0 +1,241 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Math.EC.Abc +{ + /** + * Class representing a simple version of a big decimal. A + * SimpleBigDecimal is basically a + * {@link java.math.BigInteger BigInteger} with a few digits on the right of + * the decimal point. The number of (binary) digits on the right of the decimal + * point is called the scale of the SimpleBigDecimal. + * Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted + * automatically, but must be set manually. All SimpleBigDecimals + * taking part in the same arithmetic operation must have equal scale. The + * result of a multiplication of two SimpleBigDecimals returns a + * SimpleBigDecimal with double scale. + */ + internal class SimpleBigDecimal + // : Number + { + // private static final long serialVersionUID = 1L; + + private readonly BigInteger bigInt; + private readonly int scale; + + /** + * Returns a SimpleBigDecimal representing the same numerical + * value as value. + * @param value The value of the SimpleBigDecimal to be + * created. + * @param scale The scale of the SimpleBigDecimal to be + * created. + * @return The such created SimpleBigDecimal. + */ + public static SimpleBigDecimal GetInstance(BigInteger val, int scale) + { + return new SimpleBigDecimal(val.ShiftLeft(scale), scale); + } + + /** + * Constructor for SimpleBigDecimal. The value of the + * constructed SimpleBigDecimal Equals bigInt / + * 2scale. + * @param bigInt The bigInt value parameter. + * @param scale The scale of the constructed SimpleBigDecimal. + */ + public SimpleBigDecimal(BigInteger bigInt, int scale) + { + if (scale < 0) + throw new ArgumentException("scale may not be negative"); + + this.bigInt = bigInt; + this.scale = scale; + } + + private SimpleBigDecimal(SimpleBigDecimal limBigDec) + { + bigInt = limBigDec.bigInt; + scale = limBigDec.scale; + } + + private void CheckScale(SimpleBigDecimal b) + { + if (scale != b.scale) + throw new ArgumentException("Only SimpleBigDecimal of same scale allowed in arithmetic operations"); + } + + public SimpleBigDecimal AdjustScale(int newScale) + { + if (newScale < 0) + throw new ArgumentException("scale may not be negative"); + + if (newScale == scale) + return this; + + return new SimpleBigDecimal(bigInt.ShiftLeft(newScale - scale), newScale); + } + + public SimpleBigDecimal Add(SimpleBigDecimal b) + { + CheckScale(b); + return new SimpleBigDecimal(bigInt.Add(b.bigInt), scale); + } + + public SimpleBigDecimal Add(BigInteger b) + { + return new SimpleBigDecimal(bigInt.Add(b.ShiftLeft(scale)), scale); + } + + public SimpleBigDecimal Negate() + { + return new SimpleBigDecimal(bigInt.Negate(), scale); + } + + public SimpleBigDecimal Subtract(SimpleBigDecimal b) + { + return Add(b.Negate()); + } + + public SimpleBigDecimal Subtract(BigInteger b) + { + return new SimpleBigDecimal(bigInt.Subtract(b.ShiftLeft(scale)), scale); + } + + public SimpleBigDecimal Multiply(SimpleBigDecimal b) + { + CheckScale(b); + return new SimpleBigDecimal(bigInt.Multiply(b.bigInt), scale + scale); + } + + public SimpleBigDecimal Multiply(BigInteger b) + { + return new SimpleBigDecimal(bigInt.Multiply(b), scale); + } + + public SimpleBigDecimal Divide(SimpleBigDecimal b) + { + CheckScale(b); + BigInteger dividend = bigInt.ShiftLeft(scale); + return new SimpleBigDecimal(dividend.Divide(b.bigInt), scale); + } + + public SimpleBigDecimal Divide(BigInteger b) + { + return new SimpleBigDecimal(bigInt.Divide(b), scale); + } + + public SimpleBigDecimal ShiftLeft(int n) + { + return new SimpleBigDecimal(bigInt.ShiftLeft(n), scale); + } + + public int CompareTo(SimpleBigDecimal val) + { + CheckScale(val); + return bigInt.CompareTo(val.bigInt); + } + + public int CompareTo(BigInteger val) + { + return bigInt.CompareTo(val.ShiftLeft(scale)); + } + + public BigInteger Floor() + { + return bigInt.ShiftRight(scale); + } + + public BigInteger Round() + { + SimpleBigDecimal oneHalf = new SimpleBigDecimal(BigInteger.One, 1); + return Add(oneHalf.AdjustScale(scale)).Floor(); + } + + public int IntValue + { + get { return Floor().IntValue; } + } + + public long LongValue + { + get { return Floor().LongValue; } + } + +// public double doubleValue() +// { +// return new Double(ToString()).doubleValue(); +// } +// +// public float floatValue() +// { +// return new Float(ToString()).floatValue(); +// } + + public int Scale + { + get { return scale; } + } + + public override string ToString() + { + if (scale == 0) + return bigInt.ToString(); + + BigInteger floorBigInt = Floor(); + + BigInteger fract = bigInt.Subtract(floorBigInt.ShiftLeft(scale)); + if (bigInt.SignValue < 0) + { + fract = BigInteger.One.ShiftLeft(scale).Subtract(fract); + } + + if ((floorBigInt.SignValue == -1) && (!(fract.Equals(BigInteger.Zero)))) + { + floorBigInt = floorBigInt.Add(BigInteger.One); + } + string leftOfPoint = floorBigInt.ToString(); + + char[] fractCharArr = new char[scale]; + string fractStr = fract.ToString(2); + int fractLen = fractStr.Length; + int zeroes = scale - fractLen; + for (int i = 0; i < zeroes; i++) + { + fractCharArr[i] = '0'; + } + for (int j = 0; j < fractLen; j++) + { + fractCharArr[zeroes + j] = fractStr[j]; + } + string rightOfPoint = new string(fractCharArr); + + StringBuilder sb = new StringBuilder(leftOfPoint); + sb.Append("."); + sb.Append(rightOfPoint); + + return sb.ToString(); + } + + public override bool Equals( + object obj) + { + if (this == obj) + return true; + + SimpleBigDecimal other = obj as SimpleBigDecimal; + + if (other == null) + return false; + + return bigInt.Equals(other.bigInt) + && scale == other.scale; + } + + public override int GetHashCode() + { + return bigInt.GetHashCode() ^ scale; + } + + } +} diff --git a/src/core/srcbc/math/ec/abc/Tnaf.cs b/src/core/srcbc/math/ec/abc/Tnaf.cs new file mode 100644 index 0000000..b73bcd1 --- /dev/null +++ b/src/core/srcbc/math/ec/abc/Tnaf.cs @@ -0,0 +1,834 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Abc +{ + /** + * Class holding methods for point multiplication based on the window + * τ-adic nonadjacent form (WTNAF). The algorithms are based on the + * paper "Improved Algorithms for Arithmetic on Anomalous Binary Curves" + * by Jerome A. Solinas. The paper first appeared in the Proceedings of + * Crypto 1997. + */ + internal class Tnaf + { + private static readonly BigInteger MinusOne = BigInteger.One.Negate(); + private static readonly BigInteger MinusTwo = BigInteger.Two.Negate(); + private static readonly BigInteger MinusThree = BigInteger.Three.Negate(); + private static readonly BigInteger Four = BigInteger.ValueOf(4); + + /** + * The window width of WTNAF. The standard value of 4 is slightly less + * than optimal for running time, but keeps space requirements for + * precomputation low. For typical curves, a value of 5 or 6 results in + * a better running time. When changing this value, the + * αu's must be computed differently, see + * e.g. "Guide to Elliptic Curve Cryptography", Darrel Hankerson, + * Alfred Menezes, Scott Vanstone, Springer-Verlag New York Inc., 2004, + * p. 121-122 + */ + public const sbyte Width = 4; + + /** + * 24 + */ + public const sbyte Pow2Width = 16; + + /** + * The αu's for a=0 as an array + * of ZTauElements. + */ + public static readonly ZTauElement[] Alpha0 = + { + null, + new ZTauElement(BigInteger.One, BigInteger.Zero), null, + new ZTauElement(MinusThree, MinusOne), null, + new ZTauElement(MinusOne, MinusOne), null, + new ZTauElement(BigInteger.One, MinusOne), null + }; + + /** + * The αu's for a=0 as an array + * of TNAFs. + */ + public static readonly sbyte[][] Alpha0Tnaf = + { + null, new sbyte[]{1}, null, new sbyte[]{-1, 0, 1}, null, new sbyte[]{1, 0, 1}, null, new sbyte[]{-1, 0, 0, 1} + }; + + /** + * The αu's for a=1 as an array + * of ZTauElements. + */ + public static readonly ZTauElement[] Alpha1 = + { + null, + new ZTauElement(BigInteger.One, BigInteger.Zero), null, + new ZTauElement(MinusThree, BigInteger.One), null, + new ZTauElement(MinusOne, BigInteger.One), null, + new ZTauElement(BigInteger.One, BigInteger.One), null + }; + + /** + * The αu's for a=1 as an array + * of TNAFs. + */ + public static readonly sbyte[][] Alpha1Tnaf = + { + null, new sbyte[]{1}, null, new sbyte[]{-1, 0, 1}, null, new sbyte[]{1, 0, 1}, null, new sbyte[]{-1, 0, 0, -1} + }; + + /** + * Computes the norm of an element λ of + * Z[τ]. + * @param mu The parameter μ of the elliptic curve. + * @param lambda The element λ of + * Z[τ]. + * @return The norm of λ. + */ + public static BigInteger Norm(sbyte mu, ZTauElement lambda) + { + BigInteger norm; + + // s1 = u^2 + BigInteger s1 = lambda.u.Multiply(lambda.u); + + // s2 = u * v + BigInteger s2 = lambda.u.Multiply(lambda.v); + + // s3 = 2 * v^2 + BigInteger s3 = lambda.v.Multiply(lambda.v).ShiftLeft(1); + + if (mu == 1) + { + norm = s1.Add(s2).Add(s3); + } + else if (mu == -1) + { + norm = s1.Subtract(s2).Add(s3); + } + else + { + throw new ArgumentException("mu must be 1 or -1"); + } + + return norm; + } + + /** + * Computes the norm of an element λ of + * R[τ], where λ = u + vτ + * and u and u are real numbers (elements of + * R). + * @param mu The parameter μ of the elliptic curve. + * @param u The real part of the element λ of + * R[τ]. + * @param v The τ-adic part of the element + * λ of R[τ]. + * @return The norm of λ. + */ + public static SimpleBigDecimal Norm(sbyte mu, SimpleBigDecimal u, SimpleBigDecimal v) + { + SimpleBigDecimal norm; + + // s1 = u^2 + SimpleBigDecimal s1 = u.Multiply(u); + + // s2 = u * v + SimpleBigDecimal s2 = u.Multiply(v); + + // s3 = 2 * v^2 + SimpleBigDecimal s3 = v.Multiply(v).ShiftLeft(1); + + if (mu == 1) + { + norm = s1.Add(s2).Add(s3); + } + else if (mu == -1) + { + norm = s1.Subtract(s2).Add(s3); + } + else + { + throw new ArgumentException("mu must be 1 or -1"); + } + + return norm; + } + + /** + * Rounds an element λ of R[τ] + * to an element of Z[τ], such that their difference + * has minimal norm. λ is given as + * λ = λ0 + λ1τ. + * @param lambda0 The component λ0. + * @param lambda1 The component λ1. + * @param mu The parameter μ of the elliptic curve. Must + * equal 1 or -1. + * @return The rounded element of Z[τ]. + * @throws ArgumentException if lambda0 and + * lambda1 do not have same scale. + */ + public static ZTauElement Round(SimpleBigDecimal lambda0, + SimpleBigDecimal lambda1, sbyte mu) + { + int scale = lambda0.Scale; + if (lambda1.Scale != scale) + throw new ArgumentException("lambda0 and lambda1 do not have same scale"); + + if (!((mu == 1) || (mu == -1))) + throw new ArgumentException("mu must be 1 or -1"); + + BigInteger f0 = lambda0.Round(); + BigInteger f1 = lambda1.Round(); + + SimpleBigDecimal eta0 = lambda0.Subtract(f0); + SimpleBigDecimal eta1 = lambda1.Subtract(f1); + + // eta = 2*eta0 + mu*eta1 + SimpleBigDecimal eta = eta0.Add(eta0); + if (mu == 1) + { + eta = eta.Add(eta1); + } + else + { + // mu == -1 + eta = eta.Subtract(eta1); + } + + // check1 = eta0 - 3*mu*eta1 + // check2 = eta0 + 4*mu*eta1 + SimpleBigDecimal threeEta1 = eta1.Add(eta1).Add(eta1); + SimpleBigDecimal fourEta1 = threeEta1.Add(eta1); + SimpleBigDecimal check1; + SimpleBigDecimal check2; + if (mu == 1) + { + check1 = eta0.Subtract(threeEta1); + check2 = eta0.Add(fourEta1); + } + else + { + // mu == -1 + check1 = eta0.Add(threeEta1); + check2 = eta0.Subtract(fourEta1); + } + + sbyte h0 = 0; + sbyte h1 = 0; + + // if eta >= 1 + if (eta.CompareTo(BigInteger.One) >= 0) + { + if (check1.CompareTo(MinusOne) < 0) + { + h1 = mu; + } + else + { + h0 = 1; + } + } + else + { + // eta < 1 + if (check2.CompareTo(BigInteger.Two) >= 0) + { + h1 = mu; + } + } + + // if eta < -1 + if (eta.CompareTo(MinusOne) < 0) + { + if (check1.CompareTo(BigInteger.One) >= 0) + { + h1 = (sbyte)-mu; + } + else + { + h0 = -1; + } + } + else + { + // eta >= -1 + if (check2.CompareTo(MinusTwo) < 0) + { + h1 = (sbyte)-mu; + } + } + + BigInteger q0 = f0.Add(BigInteger.ValueOf(h0)); + BigInteger q1 = f1.Add(BigInteger.ValueOf(h1)); + return new ZTauElement(q0, q1); + } + + /** + * Approximate division by n. For an integer + * k, the value λ = s k / n is + * computed to c bits of accuracy. + * @param k The parameter k. + * @param s The curve parameter s0 or + * s1. + * @param vm The Lucas Sequence element Vm. + * @param a The parameter a of the elliptic curve. + * @param m The bit length of the finite field + * Fm. + * @param c The number of bits of accuracy, i.e. the scale of the returned + * SimpleBigDecimal. + * @return The value λ = s k / n computed to + * c bits of accuracy. + */ + public static SimpleBigDecimal ApproximateDivisionByN(BigInteger k, + BigInteger s, BigInteger vm, sbyte a, int m, int c) + { + int _k = (m + 5)/2 + c; + BigInteger ns = k.ShiftRight(m - _k - 2 + a); + + BigInteger gs = s.Multiply(ns); + + BigInteger hs = gs.ShiftRight(m); + + BigInteger js = vm.Multiply(hs); + + BigInteger gsPlusJs = gs.Add(js); + BigInteger ls = gsPlusJs.ShiftRight(_k-c); + if (gsPlusJs.TestBit(_k-c-1)) + { + // round up + ls = ls.Add(BigInteger.One); + } + + return new SimpleBigDecimal(ls, c); + } + + /** + * Computes the τ-adic NAF (non-adjacent form) of an + * element λ of Z[τ]. + * @param mu The parameter μ of the elliptic curve. + * @param lambda The element λ of + * Z[τ]. + * @return The τ-adic NAF of λ. + */ + public static sbyte[] TauAdicNaf(sbyte mu, ZTauElement lambda) + { + if (!((mu == 1) || (mu == -1))) + throw new ArgumentException("mu must be 1 or -1"); + + BigInteger norm = Norm(mu, lambda); + + // Ceiling of log2 of the norm + int log2Norm = norm.BitLength; + + // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52 + int maxLength = log2Norm > 30 ? log2Norm + 4 : 34; + + // The array holding the TNAF + sbyte[] u = new sbyte[maxLength]; + int i = 0; + + // The actual length of the TNAF + int length = 0; + + BigInteger r0 = lambda.u; + BigInteger r1 = lambda.v; + + while(!((r0.Equals(BigInteger.Zero)) && (r1.Equals(BigInteger.Zero)))) + { + // If r0 is odd + if (r0.TestBit(0)) + { + u[i] = (sbyte) BigInteger.Two.Subtract((r0.Subtract(r1.ShiftLeft(1))).Mod(Four)).IntValue; + + // r0 = r0 - u[i] + if (u[i] == 1) + { + r0 = r0.ClearBit(0); + } + else + { + // u[i] == -1 + r0 = r0.Add(BigInteger.One); + } + length = i; + } + else + { + u[i] = 0; + } + + BigInteger t = r0; + BigInteger s = r0.ShiftRight(1); + if (mu == 1) + { + r0 = r1.Add(s); + } + else + { + // mu == -1 + r0 = r1.Subtract(s); + } + + r1 = t.ShiftRight(1).Negate(); + i++; + } + + length++; + + // Reduce the TNAF array to its actual length + sbyte[] tnaf = new sbyte[length]; + Array.Copy(u, 0, tnaf, 0, length); + return tnaf; + } + + /** + * Applies the operation τ() to an + * F2mPoint. + * @param p The F2mPoint to which τ() is applied. + * @return τ(p) + */ + public static F2mPoint Tau(F2mPoint p) + { + if (p.IsInfinity) + return p; + + ECFieldElement x = p.X; + ECFieldElement y = p.Y; + + return new F2mPoint(p.Curve, x.Square(), y.Square(), p.IsCompressed); + } + + /** + * Returns the parameter μ of the elliptic curve. + * @param curve The elliptic curve from which to obtain μ. + * The curve must be a Koblitz curve, i.e. a Equals + * 0 or 1 and b Equals + * 1. + * @return μ of the elliptic curve. + * @throws ArgumentException if the given ECCurve is not a Koblitz + * curve. + */ + public static sbyte GetMu(F2mCurve curve) + { + BigInteger a = curve.A.ToBigInteger(); + + sbyte mu; + if (a.SignValue == 0) + { + mu = -1; + } + else if (a.Equals(BigInteger.One)) + { + mu = 1; + } + else + { + throw new ArgumentException("No Koblitz curve (ABC), TNAF multiplication not possible"); + } + return mu; + } + + /** + * Calculates the Lucas Sequence elements Uk-1 and + * Uk or Vk-1 and + * Vk. + * @param mu The parameter μ of the elliptic curve. + * @param k The index of the second element of the Lucas Sequence to be + * returned. + * @param doV If set to true, computes Vk-1 and + * Vk, otherwise Uk-1 and + * Uk. + * @return An array with 2 elements, containing Uk-1 + * and Uk or Vk-1 + * and Vk. + */ + public static BigInteger[] GetLucas(sbyte mu, int k, bool doV) + { + if (!(mu == 1 || mu == -1)) + throw new ArgumentException("mu must be 1 or -1"); + + BigInteger u0; + BigInteger u1; + BigInteger u2; + + if (doV) + { + u0 = BigInteger.Two; + u1 = BigInteger.ValueOf(mu); + } + else + { + u0 = BigInteger.Zero; + u1 = BigInteger.One; + } + + for (int i = 1; i < k; i++) + { + // u2 = mu*u1 - 2*u0; + BigInteger s = null; + if (mu == 1) + { + s = u1; + } + else + { + // mu == -1 + s = u1.Negate(); + } + + u2 = s.Subtract(u0.ShiftLeft(1)); + u0 = u1; + u1 = u2; + // System.out.println(i + ": " + u2); + // System.out.println(); + } + + BigInteger[] retVal = {u0, u1}; + return retVal; + } + + /** + * Computes the auxiliary value tw. If the width is + * 4, then for mu = 1, tw = 6 and for + * mu = -1, tw = 10 + * @param mu The parameter μ of the elliptic curve. + * @param w The window width of the WTNAF. + * @return the auxiliary value tw + */ + public static BigInteger GetTw(sbyte mu, int w) + { + if (w == 4) + { + if (mu == 1) + { + return BigInteger.ValueOf(6); + } + else + { + // mu == -1 + return BigInteger.ValueOf(10); + } + } + else + { + // For w <> 4, the values must be computed + BigInteger[] us = GetLucas(mu, w, false); + BigInteger twoToW = BigInteger.Zero.SetBit(w); + BigInteger u1invert = us[1].ModInverse(twoToW); + BigInteger tw; + tw = BigInteger.Two.Multiply(us[0]).Multiply(u1invert).Mod(twoToW); + //System.out.println("mu = " + mu); + //System.out.println("tw = " + tw); + return tw; + } + } + + /** + * Computes the auxiliary values s0 and + * s1 used for partial modular reduction. + * @param curve The elliptic curve for which to compute + * s0 and s1. + * @throws ArgumentException if curve is not a + * Koblitz curve (Anomalous Binary Curve, ABC). + */ + public static BigInteger[] GetSi(F2mCurve curve) + { + if (!curve.IsKoblitz) + throw new ArgumentException("si is defined for Koblitz curves only"); + + int m = curve.M; + int a = curve.A.ToBigInteger().IntValue; + sbyte mu = curve.GetMu(); + int h = curve.H.IntValue; + int index = m + 3 - a; + BigInteger[] ui = GetLucas(mu, index, false); + + BigInteger dividend0; + BigInteger dividend1; + if (mu == 1) + { + dividend0 = BigInteger.One.Subtract(ui[1]); + dividend1 = BigInteger.One.Subtract(ui[0]); + } + else if (mu == -1) + { + dividend0 = BigInteger.One.Add(ui[1]); + dividend1 = BigInteger.One.Add(ui[0]); + } + else + { + throw new ArgumentException("mu must be 1 or -1"); + } + + BigInteger[] si = new BigInteger[2]; + + if (h == 2) + { + si[0] = dividend0.ShiftRight(1); + si[1] = dividend1.ShiftRight(1).Negate(); + } + else if (h == 4) + { + si[0] = dividend0.ShiftRight(2); + si[1] = dividend1.ShiftRight(2).Negate(); + } + else + { + throw new ArgumentException("h (Cofactor) must be 2 or 4"); + } + + return si; + } + + /** + * Partial modular reduction modulo + * m - 1)/(τ - 1). + * @param k The integer to be reduced. + * @param m The bitlength of the underlying finite field. + * @param a The parameter a of the elliptic curve. + * @param s The auxiliary values s0 and + * s1. + * @param mu The parameter μ of the elliptic curve. + * @param c The precision (number of bits of accuracy) of the partial + * modular reduction. + * @return ρ := k partmod (τm - 1)/(τ - 1) + */ + public static ZTauElement PartModReduction(BigInteger k, int m, sbyte a, + BigInteger[] s, sbyte mu, sbyte c) + { + // d0 = s[0] + mu*s[1]; mu is either 1 or -1 + BigInteger d0; + if (mu == 1) + { + d0 = s[0].Add(s[1]); + } + else + { + d0 = s[0].Subtract(s[1]); + } + + BigInteger[] v = GetLucas(mu, m, true); + BigInteger vm = v[1]; + + SimpleBigDecimal lambda0 = ApproximateDivisionByN( + k, s[0], vm, a, m, c); + + SimpleBigDecimal lambda1 = ApproximateDivisionByN( + k, s[1], vm, a, m, c); + + ZTauElement q = Round(lambda0, lambda1, mu); + + // r0 = n - d0*q0 - 2*s1*q1 + BigInteger r0 = k.Subtract(d0.Multiply(q.u)).Subtract( + BigInteger.ValueOf(2).Multiply(s[1]).Multiply(q.v)); + + // r1 = s1*q0 - s0*q1 + BigInteger r1 = s[1].Multiply(q.u).Subtract(s[0].Multiply(q.v)); + + return new ZTauElement(r0, r1); + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by a BigInteger using the reduced τ-adic + * NAF (RTNAF) method. + * @param p The F2mPoint to Multiply. + * @param k The BigInteger by which to Multiply p. + * @return k * p + */ + public static F2mPoint MultiplyRTnaf(F2mPoint p, BigInteger k) + { + F2mCurve curve = (F2mCurve) p.Curve; + int m = curve.M; + sbyte a = (sbyte) curve.A.ToBigInteger().IntValue; + sbyte mu = curve.GetMu(); + BigInteger[] s = curve.GetSi(); + ZTauElement rho = PartModReduction(k, m, a, s, mu, (sbyte)10); + + return MultiplyTnaf(p, rho); + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by an element λ of Z[τ] + * using the τ-adic NAF (TNAF) method. + * @param p The F2mPoint to Multiply. + * @param lambda The element λ of + * Z[τ]. + * @return λ * p + */ + public static F2mPoint MultiplyTnaf(F2mPoint p, ZTauElement lambda) + { + F2mCurve curve = (F2mCurve)p.Curve; + sbyte mu = curve.GetMu(); + sbyte[] u = TauAdicNaf(mu, lambda); + + F2mPoint q = MultiplyFromTnaf(p, u); + + return q; + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by an element λ of Z[τ] + * using the τ-adic NAF (TNAF) method, given the TNAF + * of λ. + * @param p The F2mPoint to Multiply. + * @param u The the TNAF of λ.. + * @return λ * p + */ + public static F2mPoint MultiplyFromTnaf(F2mPoint p, sbyte[] u) + { + F2mCurve curve = (F2mCurve)p.Curve; + F2mPoint q = (F2mPoint) curve.Infinity; + for (int i = u.Length - 1; i >= 0; i--) + { + q = Tau(q); + if (u[i] == 1) + { + q = (F2mPoint)q.AddSimple(p); + } + else if (u[i] == -1) + { + q = (F2mPoint)q.SubtractSimple(p); + } + } + return q; + } + + /** + * Computes the [τ]-adic window NAF of an element + * λ of Z[τ]. + * @param mu The parameter μ of the elliptic curve. + * @param lambda The element λ of + * Z[τ] of which to compute the + * [τ]-adic NAF. + * @param width The window width of the resulting WNAF. + * @param pow2w 2width. + * @param tw The auxiliary value tw. + * @param alpha The αu's for the window width. + * @return The [τ]-adic window NAF of + * λ. + */ + public static sbyte[] TauAdicWNaf(sbyte mu, ZTauElement lambda, + sbyte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha) + { + if (!((mu == 1) || (mu == -1))) + throw new ArgumentException("mu must be 1 or -1"); + + BigInteger norm = Norm(mu, lambda); + + // Ceiling of log2 of the norm + int log2Norm = norm.BitLength; + + // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52 + int maxLength = log2Norm > 30 ? log2Norm + 4 + width : 34 + width; + + // The array holding the TNAF + sbyte[] u = new sbyte[maxLength]; + + // 2^(width - 1) + BigInteger pow2wMin1 = pow2w.ShiftRight(1); + + // Split lambda into two BigIntegers to simplify calculations + BigInteger r0 = lambda.u; + BigInteger r1 = lambda.v; + int i = 0; + + // while lambda <> (0, 0) + while (!((r0.Equals(BigInteger.Zero))&&(r1.Equals(BigInteger.Zero)))) + { + // if r0 is odd + if (r0.TestBit(0)) + { + // uUnMod = r0 + r1*tw Mod 2^width + BigInteger uUnMod + = r0.Add(r1.Multiply(tw)).Mod(pow2w); + + sbyte uLocal; + // if uUnMod >= 2^(width - 1) + if (uUnMod.CompareTo(pow2wMin1) >= 0) + { + uLocal = (sbyte) uUnMod.Subtract(pow2w).IntValue; + } + else + { + uLocal = (sbyte) uUnMod.IntValue; + } + // uLocal is now in [-2^(width-1), 2^(width-1)-1] + + u[i] = uLocal; + bool s = true; + if (uLocal < 0) + { + s = false; + uLocal = (sbyte)-uLocal; + } + // uLocal is now >= 0 + + if (s) + { + r0 = r0.Subtract(alpha[uLocal].u); + r1 = r1.Subtract(alpha[uLocal].v); + } + else + { + r0 = r0.Add(alpha[uLocal].u); + r1 = r1.Add(alpha[uLocal].v); + } + } + else + { + u[i] = 0; + } + + BigInteger t = r0; + + if (mu == 1) + { + r0 = r1.Add(r0.ShiftRight(1)); + } + else + { + // mu == -1 + r0 = r1.Subtract(r0.ShiftRight(1)); + } + r1 = t.ShiftRight(1).Negate(); + i++; + } + return u; + } + + /** + * Does the precomputation for WTNAF multiplication. + * @param p The ECPoint for which to do the precomputation. + * @param a The parameter a of the elliptic curve. + * @return The precomputation array for p. + */ + public static F2mPoint[] GetPreComp(F2mPoint p, sbyte a) + { + F2mPoint[] pu; + pu = new F2mPoint[16]; + pu[1] = p; + sbyte[][] alphaTnaf; + if (a == 0) + { + alphaTnaf = Tnaf.Alpha0Tnaf; + } + else + { + // a == 1 + alphaTnaf = Tnaf.Alpha1Tnaf; + } + + int precompLen = alphaTnaf.Length; + for (int i = 3; i < precompLen; i = i + 2) + { + pu[i] = Tnaf.MultiplyFromTnaf(p, alphaTnaf[i]); + } + + return pu; + } + } +} diff --git a/src/core/srcbc/math/ec/abc/ZTauElement.cs b/src/core/srcbc/math/ec/abc/ZTauElement.cs new file mode 100644 index 0000000..3ed67ee --- /dev/null +++ b/src/core/srcbc/math/ec/abc/ZTauElement.cs @@ -0,0 +1,36 @@ +namespace Org.BouncyCastle.Math.EC.Abc +{ + /** + * Class representing an element of Z[τ]. Let + * λ be an element of Z[τ]. Then + * λ is given as λ = u + vτ. The + * components u and v may be used directly, there + * are no accessor methods. + * Immutable class. + */ + internal class ZTauElement + { + /** + * The "real" part of λ. + */ + public readonly BigInteger u; + + /** + * The "τ-adic" part of λ. + */ + public readonly BigInteger v; + + /** + * Constructor for an element λ of + * Z[τ]. + * @param u The "real" part of λ. + * @param v The "τ-adic" part of + * λ. + */ + public ZTauElement(BigInteger u, BigInteger v) + { + this.u = u; + this.v = v; + } + } +} diff --git a/src/core/srcbc/math/ec/multiplier/ECMultiplier.cs b/src/core/srcbc/math/ec/multiplier/ECMultiplier.cs new file mode 100644 index 0000000..6affc37 --- /dev/null +++ b/src/core/srcbc/math/ec/multiplier/ECMultiplier.cs @@ -0,0 +1,18 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Interface for classes encapsulating a point multiplication algorithm + * for ECPoints. + */ + internal interface ECMultiplier + { + /** + * Multiplies the ECPoint p by k, i.e. + * p is added k times to itself. + * @param p The ECPoint to be multiplied. + * @param k The factor by which p i multiplied. + * @return p multiplied by k. + */ + ECPoint Multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo); + } +} diff --git a/src/core/srcbc/math/ec/multiplier/FpNafMultiplier.cs b/src/core/srcbc/math/ec/multiplier/FpNafMultiplier.cs new file mode 100644 index 0000000..30f8242 --- /dev/null +++ b/src/core/srcbc/math/ec/multiplier/FpNafMultiplier.cs @@ -0,0 +1,39 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm. + */ + internal class FpNafMultiplier + : ECMultiplier + { + /** + * D.3.2 pg 101 + * @see org.bouncycastle.math.ec.multiplier.ECMultiplier#multiply(org.bouncycastle.math.ec.ECPoint, java.math.BigInteger) + */ + public ECPoint Multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo) + { + // TODO Probably should try to add this + // BigInteger e = k.Mod(n); // n == order of p + BigInteger e = k; + BigInteger h = e.Multiply(BigInteger.Three); + + ECPoint neg = p.Negate(); + ECPoint R = p; + + for (int i = h.BitLength - 2; i > 0; --i) + { + R = R.Twice(); + + bool hBit = h.TestBit(i); + bool eBit = e.TestBit(i); + + if (hBit != eBit) + { + R = R.Add(hBit ? p : neg); + } + } + + return R; + } + } +} diff --git a/src/core/srcbc/math/ec/multiplier/PreCompInfo.cs b/src/core/srcbc/math/ec/multiplier/PreCompInfo.cs new file mode 100644 index 0000000..77b90a2 --- /dev/null +++ b/src/core/srcbc/math/ec/multiplier/PreCompInfo.cs @@ -0,0 +1,11 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Interface for classes storing precomputation data for multiplication + * algorithms. Used as a Memento (see GOF patterns) for + * WNafMultiplier. + */ + internal interface PreCompInfo + { + } +} diff --git a/src/core/srcbc/math/ec/multiplier/ReferenceMultiplier.cs b/src/core/srcbc/math/ec/multiplier/ReferenceMultiplier.cs new file mode 100644 index 0000000..16c8778 --- /dev/null +++ b/src/core/srcbc/math/ec/multiplier/ReferenceMultiplier.cs @@ -0,0 +1,30 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + internal class ReferenceMultiplier + : ECMultiplier + { + /** + * Simple shift-and-add multiplication. Serves as reference implementation + * to verify (possibly faster) implementations in + * {@link org.bouncycastle.math.ec.ECPoint ECPoint}. + * + * @param p The point to multiply. + * @param k The factor by which to multiply. + * @return The result of the point multiplication k * p. + */ + public ECPoint Multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo) + { + ECPoint q = p.Curve.Infinity; + int t = k.BitLength; + for (int i = 0; i < t; i++) + { + if (k.TestBit(i)) + { + q = q.Add(p); + } + p = p.Twice(); + } + return q; + } + } +} diff --git a/src/core/srcbc/math/ec/multiplier/WNafMultiplier.cs b/src/core/srcbc/math/ec/multiplier/WNafMultiplier.cs new file mode 100644 index 0000000..7a08169 --- /dev/null +++ b/src/core/srcbc/math/ec/multiplier/WNafMultiplier.cs @@ -0,0 +1,241 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the WNAF (Window Non-Adjacent Form) multiplication + * algorithm. + */ + internal class WNafMultiplier + : ECMultiplier + { + /** + * Computes the Window NAF (non-adjacent Form) of an integer. + * @param width The width w of the Window NAF. The width is + * defined as the minimal number w, such that for any + * w consecutive digits in the resulting representation, at + * most one is non-zero. + * @param k The integer of which the Window NAF is computed. + * @return The Window NAF of the given width, such that the following holds: + * k = −i=0l-1 ki2i + * , where the ki denote the elements of the + * returned sbyte[]. + */ + public sbyte[] WindowNaf(sbyte width, BigInteger k) + { + // The window NAF is at most 1 element longer than the binary + // representation of the integer k. sbyte can be used instead of short or + // int unless the window width is larger than 8. For larger width use + // short or int. However, a width of more than 8 is not efficient for + // m = log2(q) smaller than 2305 Bits. Note: Values for m larger than + // 1000 Bits are currently not used in practice. + sbyte[] wnaf = new sbyte[k.BitLength + 1]; + + // 2^width as short and BigInteger + short pow2wB = (short)(1 << width); + BigInteger pow2wBI = BigInteger.ValueOf(pow2wB); + + int i = 0; + + // The actual length of the WNAF + int length = 0; + + // while k >= 1 + while (k.SignValue > 0) + { + // if k is odd + if (k.TestBit(0)) + { + // k Mod 2^width + BigInteger remainder = k.Mod(pow2wBI); + + // if remainder > 2^(width - 1) - 1 + if (remainder.TestBit(width - 1)) + { + wnaf[i] = (sbyte)(remainder.IntValue - pow2wB); + } + else + { + wnaf[i] = (sbyte)remainder.IntValue; + } + // wnaf[i] is now in [-2^(width-1), 2^(width-1)-1] + + k = k.Subtract(BigInteger.ValueOf(wnaf[i])); + length = i; + } + else + { + wnaf[i] = 0; + } + + // k = k/2 + k = k.ShiftRight(1); + i++; + } + + length++; + + // Reduce the WNAF array to its actual length + sbyte[] wnafShort = new sbyte[length]; + Array.Copy(wnaf, 0, wnafShort, 0, length); + return wnafShort; + } + + /** + * Multiplies this by an integer k using the + * Window NAF method. + * @param k The integer by which this is multiplied. + * @return A new ECPoint which equals this + * multiplied by k. + */ + public ECPoint Multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo) + { + WNafPreCompInfo wnafPreCompInfo; + + if ((preCompInfo != null) && (preCompInfo is WNafPreCompInfo)) + { + wnafPreCompInfo = (WNafPreCompInfo)preCompInfo; + } + else + { + // Ignore empty PreCompInfo or PreCompInfo of incorrect type + wnafPreCompInfo = new WNafPreCompInfo(); + } + + // floor(log2(k)) + int m = k.BitLength; + + // width of the Window NAF + sbyte width; + + // Required length of precomputation array + int reqPreCompLen; + + // Determine optimal width and corresponding length of precomputation + // array based on literature values + if (m < 13) + { + width = 2; + reqPreCompLen = 1; + } + else + { + if (m < 41) + { + width = 3; + reqPreCompLen = 2; + } + else + { + if (m < 121) + { + width = 4; + reqPreCompLen = 4; + } + else + { + if (m < 337) + { + width = 5; + reqPreCompLen = 8; + } + else + { + if (m < 897) + { + width = 6; + reqPreCompLen = 16; + } + else + { + if (m < 2305) + { + width = 7; + reqPreCompLen = 32; + } + else + { + width = 8; + reqPreCompLen = 127; + } + } + } + } + } + } + + // The length of the precomputation array + int preCompLen = 1; + + ECPoint[] preComp = wnafPreCompInfo.GetPreComp(); + ECPoint twiceP = wnafPreCompInfo.GetTwiceP(); + + // Check if the precomputed ECPoints already exist + if (preComp == null) + { + // Precomputation must be performed from scratch, create an empty + // precomputation array of desired length + preComp = new ECPoint[]{ p }; + } + else + { + // Take the already precomputed ECPoints to start with + preCompLen = preComp.Length; + } + + if (twiceP == null) + { + // Compute twice(p) + twiceP = p.Twice(); + } + + if (preCompLen < reqPreCompLen) + { + // Precomputation array must be made bigger, copy existing preComp + // array into the larger new preComp array + ECPoint[] oldPreComp = preComp; + preComp = new ECPoint[reqPreCompLen]; + Array.Copy(oldPreComp, 0, preComp, 0, preCompLen); + + for (int i = preCompLen; i < reqPreCompLen; i++) + { + // Compute the new ECPoints for the precomputation array. + // The values 1, 3, 5, ..., 2^(width-1)-1 times p are + // computed + preComp[i] = twiceP.Add(preComp[i - 1]); + } + } + + // Compute the Window NAF of the desired width + sbyte[] wnaf = WindowNaf(width, k); + int l = wnaf.Length; + + // Apply the Window NAF to p using the precomputed ECPoint values. + ECPoint q = p.Curve.Infinity; + for (int i = l - 1; i >= 0; i--) + { + q = q.Twice(); + + if (wnaf[i] != 0) + { + if (wnaf[i] > 0) + { + q = q.Add(preComp[(wnaf[i] - 1)/2]); + } + else + { + // wnaf[i] < 0 + q = q.Subtract(preComp[(-wnaf[i] - 1)/2]); + } + } + } + + // Set PreCompInfo in ECPoint, such that it is available for next + // multiplication. + wnafPreCompInfo.SetPreComp(preComp); + wnafPreCompInfo.SetTwiceP(twiceP); + p.SetPreCompInfo(wnafPreCompInfo); + return q; + } + } +} diff --git a/src/core/srcbc/math/ec/multiplier/WNafPreCompInfo.cs b/src/core/srcbc/math/ec/multiplier/WNafPreCompInfo.cs new file mode 100644 index 0000000..f7eed6c --- /dev/null +++ b/src/core/srcbc/math/ec/multiplier/WNafPreCompInfo.cs @@ -0,0 +1,46 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class holding precomputation data for the WNAF (Window Non-Adjacent Form) + * algorithm. + */ + internal class WNafPreCompInfo + : PreCompInfo + { + /** + * Array holding the precomputed ECPoints used for the Window + * NAF multiplication in + * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply() + * WNafMultiplier.multiply()}. + */ + private ECPoint[] preComp = null; + + /** + * Holds an ECPoint representing twice(this). Used for the + * Window NAF multiplication in + * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply() + * WNafMultiplier.multiply()}. + */ + private ECPoint twiceP = null; + + internal ECPoint[] GetPreComp() + { + return preComp; + } + + internal void SetPreComp(ECPoint[] preComp) + { + this.preComp = preComp; + } + + internal ECPoint GetTwiceP() + { + return twiceP; + } + + internal void SetTwiceP(ECPoint twiceThis) + { + this.twiceP = twiceThis; + } + } +} diff --git a/src/core/srcbc/math/ec/multiplier/WTauNafMultiplier.cs b/src/core/srcbc/math/ec/multiplier/WTauNafMultiplier.cs new file mode 100644 index 0000000..d005397 --- /dev/null +++ b/src/core/srcbc/math/ec/multiplier/WTauNafMultiplier.cs @@ -0,0 +1,120 @@ +using System; + +using Org.BouncyCastle.Math.EC.Abc; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the WTNAF (Window + * τ-adic Non-Adjacent Form) algorithm. + */ + internal class WTauNafMultiplier + : ECMultiplier + { + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by k using the reduced τ-adic NAF (RTNAF) + * method. + * @param p The F2mPoint to multiply. + * @param k The integer by which to multiply k. + * @return p multiplied by k. + */ + public ECPoint Multiply(ECPoint point, BigInteger k, PreCompInfo preCompInfo) + { + if (!(point is F2mPoint)) + throw new ArgumentException("Only F2mPoint can be used in WTauNafMultiplier"); + + F2mPoint p = (F2mPoint)point; + + F2mCurve curve = (F2mCurve) p.Curve; + int m = curve.M; + sbyte a = (sbyte) curve.A.ToBigInteger().IntValue; + sbyte mu = curve.GetMu(); + BigInteger[] s = curve.GetSi(); + + ZTauElement rho = Tnaf.PartModReduction(k, m, a, s, mu, (sbyte)10); + + return MultiplyWTnaf(p, rho, preCompInfo, a, mu); + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by an element λ of Z[τ] using + * the τ-adic NAF (TNAF) method. + * @param p The F2mPoint to multiply. + * @param lambda The element λ of + * Z[τ] of which to compute the + * [τ]-adic NAF. + * @return p multiplied by λ. + */ + private F2mPoint MultiplyWTnaf(F2mPoint p, ZTauElement lambda, + PreCompInfo preCompInfo, sbyte a, sbyte mu) + { + ZTauElement[] alpha; + if (a == 0) + { + alpha = Tnaf.Alpha0; + } + else + { + // a == 1 + alpha = Tnaf.Alpha1; + } + + BigInteger tw = Tnaf.GetTw(mu, Tnaf.Width); + + sbyte[]u = Tnaf.TauAdicWNaf(mu, lambda, Tnaf.Width, + BigInteger.ValueOf(Tnaf.Pow2Width), tw, alpha); + + return MultiplyFromWTnaf(p, u, preCompInfo); + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by an element λ of Z[τ] + * using the window τ-adic NAF (TNAF) method, given the + * WTNAF of λ. + * @param p The F2mPoint to multiply. + * @param u The the WTNAF of λ.. + * @return λ * p + */ + private static F2mPoint MultiplyFromWTnaf(F2mPoint p, sbyte[] u, + PreCompInfo preCompInfo) + { + F2mCurve curve = (F2mCurve)p.Curve; + sbyte a = (sbyte) curve.A.ToBigInteger().IntValue; + + F2mPoint[] pu; + if ((preCompInfo == null) || !(preCompInfo is WTauNafPreCompInfo)) + { + pu = Tnaf.GetPreComp(p, a); + p.SetPreCompInfo(new WTauNafPreCompInfo(pu)); + } + else + { + pu = ((WTauNafPreCompInfo)preCompInfo).GetPreComp(); + } + + // q = infinity + F2mPoint q = (F2mPoint) p.Curve.Infinity; + for (int i = u.Length - 1; i >= 0; i--) + { + q = Tnaf.Tau(q); + if (u[i] != 0) + { + if (u[i] > 0) + { + q = q.AddSimple(pu[u[i]]); + } + else + { + // u[i] < 0 + q = q.SubtractSimple(pu[-u[i]]); + } + } + } + + return q; + } + } +} diff --git a/src/core/srcbc/math/ec/multiplier/WTauNafPreCompInfo.cs b/src/core/srcbc/math/ec/multiplier/WTauNafPreCompInfo.cs new file mode 100644 index 0000000..07698f2 --- /dev/null +++ b/src/core/srcbc/math/ec/multiplier/WTauNafPreCompInfo.cs @@ -0,0 +1,41 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class holding precomputation data for the WTNAF (Window + * τ-adic Non-Adjacent Form) algorithm. + */ + internal class WTauNafPreCompInfo + : PreCompInfo + { + /** + * Array holding the precomputed F2mPoints used for the + * WTNAF multiplication in + * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() + * WTauNafMultiplier.multiply()}. + */ + private readonly F2mPoint[] preComp; + + /** + * Constructor for WTauNafPreCompInfo + * @param preComp Array holding the precomputed F2mPoints + * used for the WTNAF multiplication in + * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() + * WTauNafMultiplier.multiply()}. + */ + internal WTauNafPreCompInfo(F2mPoint[] preComp) + { + this.preComp = preComp; + } + + /** + * @return the array holding the precomputed F2mPoints + * used for the WTNAF multiplication in + * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() + * WTauNafMultiplier.multiply()}. + */ + internal F2mPoint[] GetPreComp() + { + return preComp; + } + } +} diff --git a/src/core/srcbc/ocsp/BasicOCSPResp.cs b/src/core/srcbc/ocsp/BasicOCSPResp.cs new file mode 100644 index 0000000..058a5fb --- /dev/null +++ b/src/core/srcbc/ocsp/BasicOCSPResp.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Ocsp +{ + /// + /// + /// BasicOcspResponse ::= SEQUENCE { + /// tbsResponseData ResponseData, + /// signatureAlgorithm AlgorithmIdentifier, + /// signature BIT STRING, + /// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL + /// } + /// + /// + public class BasicOcspResp + : X509ExtensionBase + { + private readonly BasicOcspResponse resp; + private readonly ResponseData data; +// private readonly X509Certificate[] chain; + + public BasicOcspResp( + BasicOcspResponse resp) + { + this.resp = resp; + this.data = resp.TbsResponseData; + } + + /// The DER encoding of the tbsResponseData field. + /// In the event of an encoding error. + public byte[] GetTbsResponseData() + { + try + { + return data.GetDerEncoded(); + } + catch (IOException e) + { + throw new OcspException("problem encoding tbsResponseData", e); + } + } + + public int Version + { + get { return data.Version.Value.IntValue + 1; } + } + + public RespID ResponderId + { + get { return new RespID(data.ResponderID); } + } + + public DateTime ProducedAt + { + get { return data.ProducedAt.ToDateTime(); } + } + + public SingleResp[] Responses + { + get + { + Asn1Sequence s = data.Responses; + SingleResp[] rs = new SingleResp[s.Count]; + + for (int i = 0; i != rs.Length; i++) + { + rs[i] = new SingleResp(SingleResponse.GetInstance(s[i])); + } + + return rs; + } + } + + public X509Extensions ResponseExtensions + { + get { return data.ResponseExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return ResponseExtensions; + } + + public string SignatureAlgName + { + get { return OcspUtilities.GetAlgorithmName(resp.SignatureAlgorithm.ObjectID); } + } + + public string SignatureAlgOid + { + get { return resp.SignatureAlgorithm.ObjectID.Id; } + } + + [Obsolete("RespData class is no longer required as all functionality is available on this class")] + public RespData GetResponseData() + { + return new RespData(data); + } + + public byte[] GetSignature() + { + return resp.Signature.GetBytes(); + } + + private ArrayList GetCertList() + { + // load the certificates and revocation lists if we have any + + ArrayList certs = new ArrayList(); + Asn1Sequence s = resp.Certs; + + if (s != null) + { + foreach (Asn1Encodable ae in s) + { + try + { + certs.Add(new X509CertificateParser().ReadCertificate(ae.GetEncoded())); + } + catch (IOException ex) + { + throw new OcspException("can't re-encode certificate!", ex); + } + catch (CertificateException ex) + { + throw new OcspException("can't re-encode certificate!", ex); + } + } + } + + return certs; + } + + public X509Certificate[] GetCerts() + { + ArrayList certs = GetCertList(); + + return (X509Certificate[]) certs.ToArray(typeof(X509Certificate)); + } + + /// The certificates, if any, associated with the response. + /// In the event of an encoding error. + public IX509Store GetCertificates( + string type) + { + try + { + return X509StoreFactory.Create( + "Certificate/" + type, + new X509CollectionStoreParameters(this.GetCertList())); + } + catch (Exception e) + { + throw new OcspException("can't setup the CertStore", e); + } + } + + /// + /// Verify the signature against the tbsResponseData object we contain. + /// + public bool Verify( + AsymmetricKeyParameter publicKey) + { + try + { + ISigner signature = SignerUtilities.GetSigner(this.SignatureAlgName); + signature.Init(false, publicKey); + byte[] bs = data.GetDerEncoded(); + signature.BlockUpdate(bs, 0, bs.Length); + + return signature.VerifySignature(this.GetSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing sig: " + e, e); + } + } + + /// The ASN.1 encoded representation of this object. + public byte[] GetEncoded() + { + return resp.GetEncoded(); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + BasicOcspResp other = obj as BasicOcspResp; + + if (other == null) + return false; + + return resp.Equals(other.resp); + } + + public override int GetHashCode() + { + return resp.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/ocsp/BasicOCSPRespGenerator.cs b/src/core/srcbc/ocsp/BasicOCSPRespGenerator.cs new file mode 100644 index 0000000..afa8970 --- /dev/null +++ b/src/core/srcbc/ocsp/BasicOCSPRespGenerator.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * Generator for basic OCSP response objects. + */ + public class BasicOcspRespGenerator + { + private readonly IList list = new ArrayList(); + + private X509Extensions responseExtensions; + private RespID responderID; + + private class ResponseObject + { + internal CertificateID certId; + internal CertStatus certStatus; + internal DerGeneralizedTime thisUpdate; + internal DerGeneralizedTime nextUpdate; + internal X509Extensions extensions; + + public ResponseObject( + CertificateID certId, + CertificateStatus certStatus, + DateTime thisUpdate, + X509Extensions extensions) + : this(certId, certStatus, new DerGeneralizedTime(thisUpdate), null, extensions) + { + } + + public ResponseObject( + CertificateID certId, + CertificateStatus certStatus, + DateTime thisUpdate, + DateTime nextUpdate, + X509Extensions extensions) + : this(certId, certStatus, new DerGeneralizedTime(thisUpdate), new DerGeneralizedTime(nextUpdate), extensions) + { + } + + private ResponseObject( + CertificateID certId, + CertificateStatus certStatus, + DerGeneralizedTime thisUpdate, + DerGeneralizedTime nextUpdate, + X509Extensions extensions) + { + this.certId = certId; + + if (certStatus == null) + { + this.certStatus = new CertStatus(); + } + else if (certStatus is UnknownStatus) + { + this.certStatus = new CertStatus(2, DerNull.Instance); + } + else + { + RevokedStatus rs = (RevokedStatus) certStatus; + CrlReason revocationReason = rs.HasRevocationReason + ? new CrlReason(rs.RevocationReason) + : null; + + this.certStatus = new CertStatus( + new RevokedInfo(new DerGeneralizedTime(rs.RevocationTime), revocationReason)); + } + + this.thisUpdate = thisUpdate; + this.nextUpdate = nextUpdate; + + this.extensions = extensions; + } + + public SingleResponse ToResponse() + { + return new SingleResponse(certId.ToAsn1Object(), certStatus, thisUpdate, nextUpdate, extensions); + } + } + + /** + * basic constructor + */ + public BasicOcspRespGenerator( + RespID responderID) + { + this.responderID = responderID; + } + + /** + * construct with the responderID to be the SHA-1 keyHash of the passed in public key. + */ + public BasicOcspRespGenerator( + AsymmetricKeyParameter publicKey) + { + this.responderID = new RespID(publicKey); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param certStatus status of the certificate - null if okay + */ + public void AddResponse( + CertificateID certID, + CertificateStatus certStatus) + { + list.Add(new ResponseObject(certID, certStatus, DateTime.UtcNow, null)); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public void AddResponse( + CertificateID certID, + CertificateStatus certStatus, + X509Extensions singleExtensions) + { + list.Add(new ResponseObject(certID, certStatus, DateTime.UtcNow, singleExtensions)); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param nextUpdate date when next update should be requested + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public void AddResponse( + CertificateID certID, + CertificateStatus certStatus, + DateTime nextUpdate, + X509Extensions singleExtensions) + { + list.Add(new ResponseObject(certID, certStatus, DateTime.UtcNow, nextUpdate, singleExtensions)); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param thisUpdate date this response was valid on + * @param nextUpdate date when next update should be requested + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public void AddResponse( + CertificateID certID, + CertificateStatus certStatus, + DateTime thisUpdate, + DateTime nextUpdate, + X509Extensions singleExtensions) + { + list.Add(new ResponseObject(certID, certStatus, thisUpdate, nextUpdate, singleExtensions)); + } + + /** + * Set the extensions for the response. + * + * @param responseExtensions the extension object to carry. + */ + public void SetResponseExtensions( + X509Extensions responseExtensions) + { + this.responseExtensions = responseExtensions; + } + + private BasicOcspResp GenerateResponse( + string signatureName, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + DateTime producedAt, + SecureRandom random) + { + DerObjectIdentifier signingAlgorithm; + try + { + signingAlgorithm = OcspUtilities.GetAlgorithmOid(signatureName); + } + catch (Exception e) + { + throw new ArgumentException("unknown signing algorithm specified", e); + } + + Asn1EncodableVector responses = new Asn1EncodableVector(); + + foreach (ResponseObject respObj in list) + { + try + { + responses.Add(respObj.ToResponse()); + } + catch (Exception e) + { + throw new OcspException("exception creating Request", e); + } + } + + ResponseData tbsResp = new ResponseData(responderID.ToAsn1Object(), new DerGeneralizedTime(producedAt), new DerSequence(responses), responseExtensions); + + ISigner sig = null; + + try + { + sig = SignerUtilities.GetSigner(signatureName); + + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + } + catch (Exception e) + { + throw new OcspException("exception creating signature: " + e, e); + } + + DerBitString bitSig = null; + + try + { + byte[] encoded = tbsResp.GetDerEncoded(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + bitSig = new DerBitString(sig.GenerateSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing TBSRequest: " + e, e); + } + + AlgorithmIdentifier sigAlgId = OcspUtilities.GetSigAlgID(signingAlgorithm); + + if (chain != null && chain.Length > 0) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + try + { + for (int i = 0; i != chain.Length; i++) + { + v.Add( + X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(chain[i].GetEncoded()))); + } + } + catch (IOException e) + { + throw new OcspException("error processing certs", e); + } + catch (CertificateEncodingException e) + { + throw new OcspException("error encoding certs", e); + } + + return new BasicOcspResp(new BasicOcspResponse(tbsResp, sigAlgId, bitSig, new DerSequence(v))); + } + else + { + return new BasicOcspResp(new BasicOcspResponse(tbsResp, sigAlgId, bitSig, null)); + } + } + + public BasicOcspResp Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + DateTime thisUpdate) + { + return Generate(signingAlgorithm, privateKey, chain, thisUpdate, null); + } + + public BasicOcspResp Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + DateTime producedAt, + SecureRandom random) + { + if (signingAlgorithm == null) + { + throw new ArgumentException("no signing algorithm specified"); + } + + return GenerateResponse(signingAlgorithm, privateKey, chain, producedAt, random); + } + + /** + * Return an IEnumerable of the signature names supported by the generator. + * + * @return an IEnumerable containing recognised names. + */ + public IEnumerable SignatureAlgNames + { + get { return OcspUtilities.AlgNames; } + } + } +} diff --git a/src/core/srcbc/ocsp/CertificateID.cs b/src/core/srcbc/ocsp/CertificateID.cs new file mode 100644 index 0000000..883cdfd --- /dev/null +++ b/src/core/srcbc/ocsp/CertificateID.cs @@ -0,0 +1,118 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class CertificateID + { + public const string HashSha1 = "1.3.14.3.2.26"; + + private readonly CertID id; + + public CertificateID( + CertID id) + { + this.id = id; + } + + /** + * create from an issuer certificate and the serial number of the + * certificate it signed. + * @exception OcspException if any problems occur creating the id fields. + */ + public CertificateID( + string hashAlgorithm, + X509Certificate issuerCert, + BigInteger number) + { + try + { + IDigest digest = DigestUtilities.GetDigest(hashAlgorithm); + AlgorithmIdentifier hashAlg = new AlgorithmIdentifier( + new DerObjectIdentifier(hashAlgorithm), DerNull.Instance); + + X509Name issuerName = PrincipalUtilities.GetSubjectX509Principal(issuerCert); + + byte[] encodedIssuerName = issuerName.GetEncoded(); + digest.BlockUpdate(encodedIssuerName, 0, encodedIssuerName.Length); + + byte[] hash = DigestUtilities.DoFinal(digest); + + Asn1OctetString issuerNameHash = new DerOctetString(hash); + AsymmetricKeyParameter issuerKey = issuerCert.GetPublicKey(); + + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKey); + + byte[] encodedPublicKey = info.PublicKeyData.GetBytes(); + digest.BlockUpdate(encodedPublicKey, 0, encodedPublicKey.Length); + + hash = DigestUtilities.DoFinal(digest); + + Asn1OctetString issuerKeyHash = new DerOctetString(hash); + + DerInteger serialNumber = new DerInteger(number); + + this.id = new CertID(hashAlg, issuerNameHash, issuerKeyHash, serialNumber); + } + catch (Exception e) + { + throw new OcspException("problem creating ID: " + e, e); + } + } + + public string HashAlgOid + { + get { return id.HashAlgorithm.ObjectID.Id; } + } + + public byte[] GetIssuerNameHash() + { + return id.IssuerNameHash.GetOctets(); + } + + public byte[] GetIssuerKeyHash() + { + return id.IssuerKeyHash.GetOctets(); + } + + /** + * return the serial number for the certificate associated + * with this request. + */ + public BigInteger SerialNumber + { + get { return id.SerialNumber.Value; } + } + + public CertID ToAsn1Object() + { + return id; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + CertificateID other = obj as CertificateID; + + if (other == null) + return false; + + return id.ToAsn1Object().Equals(other.id.ToAsn1Object()); + } + + public override int GetHashCode() + { + return id.ToAsn1Object().GetHashCode(); + } + } +} diff --git a/src/core/srcbc/ocsp/CertificateStatus.cs b/src/core/srcbc/ocsp/CertificateStatus.cs new file mode 100644 index 0000000..3b09f20 --- /dev/null +++ b/src/core/srcbc/ocsp/CertificateStatus.cs @@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + public abstract class CertificateStatus + { + public static readonly CertificateStatus Good = null; + } +} diff --git a/src/core/srcbc/ocsp/OCSPException.cs b/src/core/srcbc/ocsp/OCSPException.cs new file mode 100644 index 0000000..34a9ac5 --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPException.cs @@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + public class OcspException + : Exception + { + public OcspException() + { + } + + public OcspException( + string message) + : base(message) + { + } + + public OcspException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPReq.cs b/src/core/srcbc/ocsp/OCSPReq.cs new file mode 100644 index 0000000..2ce7286 --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPReq.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Ocsp +{ + /** + *
    +	 * OcspRequest     ::=     SEQUENCE {
    +	 *       tbsRequest                  TBSRequest,
    +	 *       optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
    +	 *
    +	 *   TBSRequest      ::=     SEQUENCE {
    +	 *       version             [0]     EXPLICIT Version DEFAULT v1,
    +	 *       requestorName       [1]     EXPLICIT GeneralName OPTIONAL,
    +	 *       requestList                 SEQUENCE OF Request,
    +	 *       requestExtensions   [2]     EXPLICIT Extensions OPTIONAL }
    +	 *
    +	 *   Signature       ::=     SEQUENCE {
    +	 *       signatureAlgorithm      AlgorithmIdentifier,
    +	 *       signature               BIT STRING,
    +	 *       certs               [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL}
    +	 *
    +	 *   Version         ::=             INTEGER  {  v1(0) }
    +	 *
    +	 *   Request         ::=     SEQUENCE {
    +	 *       reqCert                     CertID,
    +	 *       singleRequestExtensions     [0] EXPLICIT Extensions OPTIONAL }
    +	 *
    +	 *   CertID          ::=     SEQUENCE {
    +	 *       hashAlgorithm       AlgorithmIdentifier,
    +	 *       issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
    +	 *       issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
    +	 *       serialNumber        CertificateSerialNumber }
    +	 * 
    + */ + public class OcspReq + : X509ExtensionBase + { + private OcspRequest req; + + public OcspReq( + OcspRequest req) + { + this.req = req; + } + + public OcspReq( + byte[] req) + : this(new Asn1InputStream(req)) + { + } + + public OcspReq( + Stream inStr) + : this(new Asn1InputStream(inStr)) + { + } + + private OcspReq( + Asn1InputStream aIn) + { + try + { + this.req = OcspRequest.GetInstance(aIn.ReadObject()); + } + catch (ArgumentException e) + { + throw new IOException("malformed request: " + e.Message); + } + catch (InvalidCastException e) + { + throw new IOException("malformed request: " + e.Message); + } + } + + /** + * Return the DER encoding of the tbsRequest field. + * @return DER encoding of tbsRequest + * @throws OcspException in the event of an encoding error. + */ + public byte[] GetTbsRequest() + { + try + { + return req.TbsRequest.GetEncoded(); + } + catch (IOException e) + { + throw new OcspException("problem encoding tbsRequest", e); + } + } + + public int Version + { + get { return req.TbsRequest.Version.Value.IntValue + 1; } + } + + public GeneralName RequestorName + { + get { return GeneralName.GetInstance(req.TbsRequest.RequestorName); } + } + + public Req[] GetRequestList() + { + Asn1Sequence seq = req.TbsRequest.RequestList; + Req[] requests = new Req[seq.Count]; + + for (int i = 0; i != requests.Length; i++) + { + requests[i] = new Req(Request.GetInstance(seq[i])); + } + + return requests; + } + + public X509Extensions RequestExtensions + { + get { return X509Extensions.GetInstance(req.TbsRequest.RequestExtensions); } + } + + protected override X509Extensions GetX509Extensions() + { + return RequestExtensions; + } + + /** + * return the object identifier representing the signature algorithm + */ + public string SignatureAlgOid + { + get + { + if (!this.IsSigned) + return null; + + return req.OptionalSignature.SignatureAlgorithm.ObjectID.Id; + } + } + + public byte[] GetSignature() + { + if (!this.IsSigned) + return null; + + return req.OptionalSignature.SignatureValue.GetBytes(); + } + + private ArrayList GetCertList() + { + // load the certificates if we have any + + ArrayList certs = new ArrayList(); + Asn1Sequence s = req.OptionalSignature.Certs; + + if (s != null) + { + foreach (Asn1Encodable ae in s) + { + try + { + certs.Add(new X509CertificateParser().ReadCertificate(ae.GetEncoded())); + } + catch (Exception e) + { + throw new OcspException("can't re-encode certificate!", e); + } + } + } + + return certs; + } + + public X509Certificate[] GetCerts() + { + if (!this.IsSigned) + return null; + + ArrayList certs = this.GetCertList(); + + return (X509Certificate[]) certs.ToArray(typeof(X509Certificate)); + } + + /** + * If the request is signed return a possibly empty CertStore containing the certificates in the + * request. If the request is not signed the method returns null. + * + * @return null if not signed, a CertStore otherwise + * @throws OcspException + */ + public IX509Store GetCertificates( + string type) + { + if (!this.IsSigned) + return null; + + try + { + return X509StoreFactory.Create( + "Certificate/" + type, + new X509CollectionStoreParameters(this.GetCertList())); + } + catch (Exception e) + { + throw new OcspException("can't setup the CertStore", e); + } + } + + /** + * Return whether or not this request is signed. + * + * @return true if signed false otherwise. + */ + public bool IsSigned + { + get { return req.OptionalSignature != null; } + } + + /** + * Verify the signature against the TBSRequest object we contain. + */ + public bool Verify( + AsymmetricKeyParameter publicKey) + { + if (!this.IsSigned) + throw new OcspException("attempt to Verify signature on unsigned object"); + + try + { + ISigner signature = SignerUtilities.GetSigner(this.SignatureAlgOid); + + signature.Init(false, publicKey); + + byte[] encoded = req.TbsRequest.GetEncoded(); + + signature.BlockUpdate(encoded, 0, encoded.Length); + + return signature.VerifySignature(this.GetSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing sig: " + e, e); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return req.GetEncoded(); + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPReqGenerator.cs b/src/core/srcbc/ocsp/OCSPReqGenerator.cs new file mode 100644 index 0000000..62bb13a --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPReqGenerator.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class OcspReqGenerator + { + private IList list = new ArrayList(); + private GeneralName requestorName = null; + private X509Extensions requestExtensions = null; + + private class RequestObject + { + internal CertificateID certId; + internal X509Extensions extensions; + + public RequestObject( + CertificateID certId, + X509Extensions extensions) + { + this.certId = certId; + this.extensions = extensions; + } + + public Request ToRequest() + { + return new Request(certId.ToAsn1Object(), extensions); + } + } + + /** + * Add a request for the given CertificateID. + * + * @param certId certificate ID of interest + */ + public void AddRequest( + CertificateID certId) + { + list.Add(new RequestObject(certId, null)); + } + + /** + * Add a request with extensions + * + * @param certId certificate ID of interest + * @param singleRequestExtensions the extensions to attach to the request + */ + public void AddRequest( + CertificateID certId, + X509Extensions singleRequestExtensions) + { + list.Add(new RequestObject(certId, singleRequestExtensions)); + } + + /** + * Set the requestor name to the passed in X509Principal + * + * @param requestorName a X509Principal representing the requestor name. + */ + public void SetRequestorName( + X509Name requestorName) + { + try + { + this.requestorName = new GeneralName(GeneralName.DirectoryName, requestorName); + } + catch (Exception e) + { + throw new ArgumentException("cannot encode principal", e); + } + } + + public void SetRequestorName( + GeneralName requestorName) + { + this.requestorName = requestorName; + } + + public void SetRequestExtensions( + X509Extensions requestExtensions) + { + this.requestExtensions = requestExtensions; + } + + private OcspReq GenerateRequest( + DerObjectIdentifier signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + SecureRandom random) + { + Asn1EncodableVector requests = new Asn1EncodableVector(); + + foreach (RequestObject reqObj in list) + { + try + { + requests.Add(reqObj.ToRequest()); + } + catch (Exception e) + { + throw new OcspException("exception creating Request", e); + } + } + + TbsRequest tbsReq = new TbsRequest(requestorName, new DerSequence(requests), requestExtensions); + + ISigner sig = null; + Signature signature = null; + + if (signingAlgorithm != null) + { + if (requestorName == null) + { + throw new OcspException("requestorName must be specified if request is signed."); + } + + try + { + sig = SignerUtilities.GetSigner(signingAlgorithm.Id); + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + } + catch (Exception e) + { + throw new OcspException("exception creating signature: " + e, e); + } + + DerBitString bitSig = null; + + try + { + byte[] encoded = tbsReq.GetEncoded(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + bitSig = new DerBitString(sig.GenerateSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing TBSRequest: " + e, e); + } + + AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(signingAlgorithm, DerNull.Instance); + + if (chain != null && chain.Length > 0) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + try + { + for (int i = 0; i != chain.Length; i++) + { + v.Add( + X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(chain[i].GetEncoded()))); + } + } + catch (IOException e) + { + throw new OcspException("error processing certs", e); + } + catch (CertificateEncodingException e) + { + throw new OcspException("error encoding certs", e); + } + + signature = new Signature(sigAlgId, bitSig, new DerSequence(v)); + } + else + { + signature = new Signature(sigAlgId, bitSig); + } + } + + return new OcspReq(new OcspRequest(tbsReq, signature)); + } + + /** + * Generate an unsigned request + * + * @return the OcspReq + * @throws OcspException + */ + public OcspReq Generate() + { + return GenerateRequest(null, null, null, null); + } + + public OcspReq Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain) + { + return Generate(signingAlgorithm, privateKey, chain, null); + } + + public OcspReq Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + SecureRandom random) + { + if (signingAlgorithm == null) + throw new ArgumentException("no signing algorithm specified"); + + try + { + DerObjectIdentifier oid = OcspUtilities.GetAlgorithmOid(signingAlgorithm); + + return GenerateRequest(oid, privateKey, chain, random); + } + catch (ArgumentException) + { + throw new ArgumentException("unknown signing algorithm specified: " + signingAlgorithm); + } + } + + /** + * Return an IEnumerable of the signature names supported by the generator. + * + * @return an IEnumerable containing recognised names. + */ + public IEnumerable SignatureAlgNames + { + get { return OcspUtilities.AlgNames; } + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPResp.cs b/src/core/srcbc/ocsp/OCSPResp.cs new file mode 100644 index 0000000..13e3f72 --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPResp.cs @@ -0,0 +1,100 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Ocsp +{ + public class OcspResp + { + private OcspResponse resp; + + public OcspResp( + OcspResponse resp) + { + this.resp = resp; + } + + public OcspResp( + byte[] resp) + : this(new Asn1InputStream(resp)) + { + } + + public OcspResp( + Stream inStr) + : this(new Asn1InputStream(inStr)) + { + } + + private OcspResp( + Asn1InputStream aIn) + { + try + { + this.resp = OcspResponse.GetInstance(aIn.ReadObject()); + } + catch (Exception e) + { + throw new IOException("malformed response: " + e.Message, e); + } + } + + public int Status + { + get { return this.resp.ResponseStatus.Value.IntValue; } + } + + public object GetResponseObject() + { + ResponseBytes rb = this.resp.ResponseBytes; + + if (rb == null) + return null; + + if (rb.ResponseType.Equals(OcspObjectIdentifiers.PkixOcspBasic)) + { + try + { + return new BasicOcspResp( + BasicOcspResponse.GetInstance( + Asn1Object.FromByteArray(rb.Response.GetOctets()))); + } + catch (Exception e) + { + throw new OcspException("problem decoding object: " + e, e); + } + } + + return rb.Response; + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return resp.GetEncoded(); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + OcspResp other = obj as OcspResp; + + if (other == null) + return false; + + return resp.Equals(other.resp); + } + + public override int GetHashCode() + { + return resp.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPRespGenerator.cs b/src/core/srcbc/ocsp/OCSPRespGenerator.cs new file mode 100644 index 0000000..61d6f5f --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPRespGenerator.cs @@ -0,0 +1,54 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * base generator for an OCSP response - at the moment this only supports the + * generation of responses containing BasicOCSP responses. + */ + public class OCSPRespGenerator + { + public const int Successful = 0; // Response has valid confirmations + public const int MalformedRequest = 1; // Illegal confirmation request + public const int InternalError = 2; // Internal error in issuer + public const int TryLater = 3; // Try again later + // (4) is not used + public const int SigRequired = 5; // Must sign the request + public const int Unauthorized = 6; // Request unauthorized + + public OcspResp Generate( + int status, + object response) + { + if (response == null) + { + return new OcspResp(new OcspResponse(new OcspResponseStatus(status),null)); + } + if (response is BasicOcspResp) + { + BasicOcspResp r = (BasicOcspResp)response; + Asn1OctetString octs; + + try + { + octs = new DerOctetString(r.GetEncoded()); + } + catch (Exception e) + { + throw new OcspException("can't encode object.", e); + } + + ResponseBytes rb = new ResponseBytes( + OcspObjectIdentifiers.PkixOcspBasic, octs); + + return new OcspResp(new OcspResponse( + new OcspResponseStatus(status), rb)); + } + + throw new OcspException("unknown response object"); + } + } +} diff --git a/src/core/srcbc/ocsp/OCSPRespStatus.cs b/src/core/srcbc/ocsp/OCSPRespStatus.cs new file mode 100644 index 0000000..bef915e --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPRespStatus.cs @@ -0,0 +1,22 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + [Obsolete("Use version with correct spelling 'OcspRespStatus'")] + public abstract class OcscpRespStatus : OcspRespStatus + { + } + + public abstract class OcspRespStatus + { + /** + * note 4 is not used. + */ + public const int Successful = 0; // --Response has valid confirmations + public const int MalformedRequest = 1; // --Illegal confirmation request + public const int InternalError = 2; // --Internal error in issuer + public const int TryLater = 3; // --Try again later + public const int SigRequired = 5; // --Must sign the request + public const int Unauthorized = 6; // --Request unauthorized + } +} diff --git a/src/core/srcbc/ocsp/OCSPUtil.cs b/src/core/srcbc/ocsp/OCSPUtil.cs new file mode 100644 index 0000000..8704c74 --- /dev/null +++ b/src/core/srcbc/ocsp/OCSPUtil.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Ocsp +{ + class OcspUtilities + { + private static readonly Hashtable algorithms = new Hashtable(); + private static readonly Hashtable oids = new Hashtable(); + private static readonly ISet noParams = new HashSet(); + + static OcspUtilities() + { + algorithms.Add("MD2WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD2WITHRSA", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD5WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("MD5WITHRSA", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("SHA1WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA1WITHRSA", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("SHA1WITHDSA", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("DSAWITHSHA1", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224); + algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256); + algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224); + algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256); + algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384); + algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512); + algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + + oids.Add(PkcsObjectIdentifiers.MD2WithRsaEncryption, "MD2WITHRSA"); + oids.Add(PkcsObjectIdentifiers.MD5WithRsaEncryption, "MD5WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption, "SHA1WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160, "RIPEMD160WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128, "RIPEMD128WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256, "RIPEMD256WITHRSA"); + oids.Add(X9ObjectIdentifiers.IdDsaWithSha1, "SHA1WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha224, "SHA224WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha256, "SHA256WITHDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512WITHECDSA"); + oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, "GOST3411WITHGOST3410"); + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add(NistObjectIdentifiers.DsaWithSha224); + noParams.Add(NistObjectIdentifiers.DsaWithSha256); + } + + internal static DerObjectIdentifier GetAlgorithmOid( + string algorithmName) + { + algorithmName = algorithmName.ToUpper(CultureInfo.InvariantCulture); + + if (algorithms.ContainsKey(algorithmName)) + { + return (DerObjectIdentifier)algorithms[algorithmName]; + } + + return new DerObjectIdentifier(algorithmName); + } + + + internal static string GetAlgorithmName( + DerObjectIdentifier oid) + { + if (oids.ContainsKey(oid)) + { + return (string)oids[oid]; + } + + return oid.Id; + } + + internal static AlgorithmIdentifier GetSigAlgID( + DerObjectIdentifier sigOid) + { + if (noParams.Contains(sigOid)) + { + return new AlgorithmIdentifier(sigOid); + } + + return new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + + internal static IEnumerable AlgNames + { + get { return new EnumerableProxy(algorithms.Keys); } + } + } +} diff --git a/src/core/srcbc/ocsp/Req.cs b/src/core/srcbc/ocsp/Req.cs new file mode 100644 index 0000000..df66e0a --- /dev/null +++ b/src/core/srcbc/ocsp/Req.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class Req + : X509ExtensionBase + { + private Request req; + + public Req( + Request req) + { + this.req = req; + } + + public CertificateID GetCertID() + { + return new CertificateID(req.ReqCert); + } + + public X509Extensions SingleRequestExtensions + { + get { return req.SingleRequestExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return SingleRequestExtensions; + } + } +} diff --git a/src/core/srcbc/ocsp/RespData.cs b/src/core/srcbc/ocsp/RespData.cs new file mode 100644 index 0000000..f1ee2e9 --- /dev/null +++ b/src/core/srcbc/ocsp/RespData.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class RespData + : X509ExtensionBase + { + internal readonly ResponseData data; + + public RespData( + ResponseData data) + { + this.data = data; + } + + public int Version + { + get { return data.Version.Value.IntValue + 1; } + } + + public RespID GetResponderId() + { + return new RespID(data.ResponderID); + } + + public DateTime ProducedAt + { + get { return data.ProducedAt.ToDateTime(); } + } + + public SingleResp[] GetResponses() + { + Asn1Sequence s = data.Responses; + SingleResp[] rs = new SingleResp[s.Count]; + + for (int i = 0; i != rs.Length; i++) + { + rs[i] = new SingleResp(SingleResponse.GetInstance(s[i])); + } + + return rs; + } + + public X509Extensions ResponseExtensions + { + get { return data.ResponseExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return ResponseExtensions; + } + } +} diff --git a/src/core/srcbc/ocsp/RespID.cs b/src/core/srcbc/ocsp/RespID.cs new file mode 100644 index 0000000..56018a8 --- /dev/null +++ b/src/core/srcbc/ocsp/RespID.cs @@ -0,0 +1,86 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * Carrier for a ResponderID. + */ + public class RespID + { + internal readonly ResponderID id; + + public RespID( + ResponderID id) + { + this.id = id; + } + + public RespID( + X509Name name) + { + try + { + this.id = new ResponderID(name); + } + catch (Exception e) + { + throw new ArgumentException("can't decode name.", e); + } + } + + public RespID( + AsymmetricKeyParameter publicKey) + { + try + { + IDigest digest = DigestUtilities.GetDigest("SHA1"); + + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey); + + byte[] encoded = info.PublicKeyData.GetBytes(); + digest.BlockUpdate(encoded, 0, encoded.Length); + + byte[] hash = DigestUtilities.DoFinal(digest); + + Asn1OctetString keyHash = new DerOctetString(hash); + + this.id = new ResponderID(keyHash); + } + catch (Exception e) + { + throw new OcspException("problem creating ID: " + e, e); + } + } + + public ResponderID ToAsn1Object() + { + return id; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + RespID other = obj as RespID; + + if (other == null) + return false; + + return id.Equals(other.id); + } + + public override int GetHashCode() + { + return id.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/ocsp/RevokedStatus.cs b/src/core/srcbc/ocsp/RevokedStatus.cs new file mode 100644 index 0000000..07925af --- /dev/null +++ b/src/core/srcbc/ocsp/RevokedStatus.cs @@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * wrapper for the RevokedInfo object + */ + public class RevokedStatus + : CertificateStatus + { + internal readonly RevokedInfo info; + + public RevokedStatus( + RevokedInfo info) + { + this.info = info; + } + + public RevokedStatus( + DateTime revocationDate, + int reason) + { + this.info = new RevokedInfo(new DerGeneralizedTime(revocationDate), new CrlReason(reason)); + } + + public DateTime RevocationTime + { + get { return info.RevocationTime.ToDateTime(); } + } + + public bool HasRevocationReason + { + get { return (info.RevocationReason != null); } + } + + /** + * return the revocation reason. Note: this field is optional, test for it + * with hasRevocationReason() first. + * @exception InvalidOperationException if a reason is asked for and none is avaliable + */ + public int RevocationReason + { + get + { + if (info.RevocationReason == null) + { + throw new InvalidOperationException("attempt to get a reason where none is available"); + } + + return info.RevocationReason.Value.IntValue; + } + } + } +} diff --git a/src/core/srcbc/ocsp/SingleResp.cs b/src/core/srcbc/ocsp/SingleResp.cs new file mode 100644 index 0000000..c02390e --- /dev/null +++ b/src/core/srcbc/ocsp/SingleResp.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class SingleResp + : X509ExtensionBase + { + internal readonly SingleResponse resp; + + public SingleResp( + SingleResponse resp) + { + this.resp = resp; + } + + public CertificateID GetCertID() + { + return new CertificateID(resp.CertId); + } + + /** + * Return the status object for the response - null indicates good. + * + * @return the status object for the response, null if it is good. + */ + public object GetCertStatus() + { + CertStatus s = resp.CertStatus; + + if (s.TagNo == 0) + { + return null; // good + } + + if (s.TagNo == 1) + { + return new RevokedStatus(RevokedInfo.GetInstance(s.Status)); + } + + return new UnknownStatus(); + } + + public DateTime ThisUpdate + { + get { return resp.ThisUpdate.ToDateTime(); } + } + + /** + * return the NextUpdate value - note: this is an optional field so may + * be returned as null. + * + * @return nextUpdate, or null if not present. + */ + public DateTimeObject NextUpdate + { + get + { + return resp.NextUpdate == null + ? null + : new DateTimeObject(resp.NextUpdate.ToDateTime()); + } + } + + public X509Extensions SingleExtensions + { + get { return resp.SingleExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return SingleExtensions; + } + } +} diff --git a/src/core/srcbc/ocsp/UnknownStatus.cs b/src/core/srcbc/ocsp/UnknownStatus.cs new file mode 100644 index 0000000..e120b48 --- /dev/null +++ b/src/core/srcbc/ocsp/UnknownStatus.cs @@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * wrapper for the UnknownInfo object + */ + public class UnknownStatus + : CertificateStatus + { + public UnknownStatus() + { + } + } +} diff --git a/src/core/srcbc/openpgp/IStreamGenerator.cs b/src/core/srcbc/openpgp/IStreamGenerator.cs new file mode 100644 index 0000000..ebaf7ff --- /dev/null +++ b/src/core/srcbc/openpgp/IStreamGenerator.cs @@ -0,0 +1,7 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public interface IStreamGenerator + { + void Close(); + } +} diff --git a/src/core/srcbc/openpgp/PGPKeyRing.cs b/src/core/srcbc/openpgp/PGPKeyRing.cs new file mode 100644 index 0000000..a919e22 --- /dev/null +++ b/src/core/srcbc/openpgp/PGPKeyRing.cs @@ -0,0 +1,77 @@ +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public abstract class PgpKeyRing + : PgpObject + { + internal PgpKeyRing() + { + } + + internal static TrustPacket ReadOptionalTrustPacket( + BcpgInputStream bcpgInput) + { + return (bcpgInput.NextPacketTag() == PacketTag.Trust) + ? (TrustPacket) bcpgInput.ReadPacket() + : null; + } + + internal static ArrayList ReadSignaturesAndTrust( + BcpgInputStream bcpgInput) + { + try + { + ArrayList sigList = new ArrayList(); + + while (bcpgInput.NextPacketTag() == PacketTag.Signature) + { + SignaturePacket signaturePacket = (SignaturePacket) bcpgInput.ReadPacket(); + TrustPacket trustPacket = ReadOptionalTrustPacket(bcpgInput); + + sigList.Add(new PgpSignature(signaturePacket, trustPacket)); + } + + return sigList; + } + catch (PgpException e) + { + throw new IOException("can't create signature object: " + e.Message, e); + } + } + + internal static void ReadUserIDs( + BcpgInputStream bcpgInput, + out ArrayList ids, + out ArrayList idTrusts, + out ArrayList idSigs) + { + ids = new ArrayList(); + idTrusts = new ArrayList(); + idSigs = new ArrayList(); + + while (bcpgInput.NextPacketTag() == PacketTag.UserId + || bcpgInput.NextPacketTag() == PacketTag.UserAttribute) + { + Packet obj = bcpgInput.ReadPacket(); + if (obj is UserIdPacket) + { + UserIdPacket id = (UserIdPacket)obj; + ids.Add(id.GetId()); + } + else + { + UserAttributePacket user = (UserAttributePacket) obj; + ids.Add(new PgpUserAttributeSubpacketVector(user.GetSubpackets())); + } + + idTrusts.Add( + ReadOptionalTrustPacket(bcpgInput)); + + idSigs.Add( + ReadSignaturesAndTrust(bcpgInput)); + } + } + } +} diff --git a/src/core/srcbc/openpgp/PGPObject.cs b/src/core/srcbc/openpgp/PGPObject.cs new file mode 100644 index 0000000..624f8d0 --- /dev/null +++ b/src/core/srcbc/openpgp/PGPObject.cs @@ -0,0 +1,9 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public abstract class PgpObject + { + internal PgpObject() + { + } + } +} diff --git a/src/core/srcbc/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs b/src/core/srcbc/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs new file mode 100644 index 0000000..e8fc515 --- /dev/null +++ b/src/core/srcbc/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Bcpg.Attr; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public class PgpUserAttributeSubpacketVectorGenerator + { + private ArrayList list = new ArrayList(); + + public virtual void SetImageAttribute( + ImageAttrib.Format imageType, + byte[] imageData) + { + if (imageData == null) + throw new ArgumentException("attempt to set null image", "imageData"); + + list.Add(new ImageAttrib(imageType, imageData)); + } + + public virtual PgpUserAttributeSubpacketVector Generate() + { + return new PgpUserAttributeSubpacketVector( + (UserAttributeSubpacket[]) list.ToArray(typeof(UserAttributeSubpacket))); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpCompressedData.cs b/src/core/srcbc/openpgp/PgpCompressedData.cs new file mode 100644 index 0000000..5fbfaff --- /dev/null +++ b/src/core/srcbc/openpgp/PgpCompressedData.cs @@ -0,0 +1,50 @@ +using System.IO; + +using Org.BouncyCastle.Apache.Bzip2; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Compressed data objects + public class PgpCompressedData + : PgpObject + { + private readonly CompressedDataPacket data; + + public PgpCompressedData( + BcpgInputStream bcpgInput) + { + data = (CompressedDataPacket) bcpgInput.ReadPacket(); + } + + /// The algorithm used for compression + public CompressionAlgorithmTag Algorithm + { + get { return data.Algorithm; } + } + + /// Get the raw input stream contained in the object. + public Stream GetInputStream() + { + return data.GetInputStream(); + } + + /// Return an uncompressed input stream which allows reading of the compressed data. + public Stream GetDataStream() + { + switch (Algorithm) + { + case CompressionAlgorithmTag.Uncompressed: + return GetInputStream(); + case CompressionAlgorithmTag.Zip: + return new ZInflaterInputStream(GetInputStream(), true); + case CompressionAlgorithmTag.ZLib: + return new ZInflaterInputStream(GetInputStream()); + case CompressionAlgorithmTag.BZip2: + return new CBZip2InputStream(GetInputStream()); + default: + throw new PgpException("can't recognise compression algorithm: " + Algorithm); + } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpCompressedDataGenerator.cs b/src/core/srcbc/openpgp/PgpCompressedDataGenerator.cs new file mode 100644 index 0000000..a4c0ed8 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpCompressedDataGenerator.cs @@ -0,0 +1,177 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Apache.Bzip2; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Class for producing compressed data packets. + public class PgpCompressedDataGenerator + : IStreamGenerator + { + private readonly CompressionAlgorithmTag algorithm; + private readonly int compression; + + private Stream dOut; + private BcpgOutputStream pkOut; + + public PgpCompressedDataGenerator( + CompressionAlgorithmTag algorithm) + : this(algorithm, JZlib.Z_DEFAULT_COMPRESSION) + { + } + + public PgpCompressedDataGenerator( + CompressionAlgorithmTag algorithm, + int compression) + { + switch (algorithm) + { + case CompressionAlgorithmTag.Uncompressed: + case CompressionAlgorithmTag.Zip: + case CompressionAlgorithmTag.ZLib: + case CompressionAlgorithmTag.BZip2: + break; + default: + throw new ArgumentException("unknown compression algorithm", "algorithm"); + } + + if (compression != JZlib.Z_DEFAULT_COMPRESSION) + { + if ((compression < JZlib.Z_NO_COMPRESSION) || (compression > JZlib.Z_BEST_COMPRESSION)) + { + throw new ArgumentException("unknown compression level: " + compression); + } + } + + this.algorithm = algorithm; + this.compression = compression; + } + + /// + ///

    + /// Return an output stream which will save the data being written to + /// the compressed object. + ///

    + ///

    + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter outStr. + ///

    + ///
    + /// Stream to be used for output. + /// A Stream for output of the compressed data. + /// + /// + /// + public Stream Open( + Stream outStr) + { + if (dOut != null) + throw new InvalidOperationException("generator already in open state"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + + this.pkOut = new BcpgOutputStream(outStr, PacketTag.CompressedData); + + doOpen(); + + return new WrappedGeneratorStream(this, dOut); + } + + /// + ///

    + /// Return an output stream which will compress the data as it is written to it. + /// The stream will be written out in chunks according to the size of the passed in buffer. + ///

    + ///

    + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter outStr. + ///

    + ///

    + /// Note: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used. + ///

    + ///

    + /// Note: using this may break compatibility with RFC 1991 compliant tools. + /// Only recent OpenPGP implementations are capable of accepting these streams. + ///

    + ///
    + /// Stream to be used for output. + /// The buffer to use. + /// A Stream for output of the compressed data. + /// + /// + /// + /// + public Stream Open( + Stream outStr, + byte[] buffer) + { + if (dOut != null) + throw new InvalidOperationException("generator already in open state"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + if (buffer == null) + throw new ArgumentNullException("buffer"); + + this.pkOut = new BcpgOutputStream(outStr, PacketTag.CompressedData, buffer); + + doOpen(); + + return new WrappedGeneratorStream(this, dOut); + } + + private void doOpen() + { + pkOut.WriteByte((byte) algorithm); + + switch (algorithm) + { + case CompressionAlgorithmTag.Uncompressed: + dOut = pkOut; + break; + case CompressionAlgorithmTag.Zip: + dOut = new ZDeflaterOutputStream(pkOut, compression, true); + break; + case CompressionAlgorithmTag.ZLib: + dOut = new ZDeflaterOutputStream(pkOut, compression, false); + break; + case CompressionAlgorithmTag.BZip2: + dOut = new CBZip2OutputStream(pkOut); + break; + default: + // Constructor should guard against this possibility + throw new InvalidOperationException(); + } + } + + /// Close the compressed object.summary> + public void Close() + { + if (dOut != null) + { + switch (algorithm) + { + case CompressionAlgorithmTag.BZip2: + ((CBZip2OutputStream) dOut).Finish(); + break; + case CompressionAlgorithmTag.Zip: + case CompressionAlgorithmTag.ZLib: + ((ZDeflaterOutputStream) dOut).Finish(); + break; + } + + dOut.Flush(); + + pkOut.Finish(); + pkOut.Flush(); + + dOut = null; + pkOut = null; + } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpDataValidationException.cs b/src/core/srcbc/openpgp/PgpDataValidationException.cs new file mode 100644 index 0000000..a38c368 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpDataValidationException.cs @@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// Thrown if the IV at the start of a data stream indicates the wrong key is being used. + /// + public class PgpDataValidationException + : PgpException + { + public PgpDataValidationException() : base() {} + public PgpDataValidationException(string message) : base(message) {} + public PgpDataValidationException(string message, Exception exception) : base(message, exception) {} + } +} diff --git a/src/core/srcbc/openpgp/PgpEncryptedData.cs b/src/core/srcbc/openpgp/PgpEncryptedData.cs new file mode 100644 index 0000000..982ad72 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpEncryptedData.cs @@ -0,0 +1,151 @@ +using System; +using System.Diagnostics; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public abstract class PgpEncryptedData + { + internal class TruncatedStream + : BaseInputStream + { + private const int LookAheadSize = 22; + private const int LookAheadBufSize = 512; + private const int LookAheadBufLimit = LookAheadBufSize - LookAheadSize; + + private readonly Stream inStr; + private readonly byte[] lookAhead = new byte[LookAheadBufSize]; + private int bufStart, bufEnd; + + internal TruncatedStream( + Stream inStr) + { + int numRead = Streams.ReadFully(inStr, lookAhead, 0, lookAhead.Length); + + if (numRead < LookAheadSize) + throw new EndOfStreamException(); + + this.inStr = inStr; + this.bufStart = 0; + this.bufEnd = numRead - LookAheadSize; + } + + private int FillBuffer() + { + if (bufEnd < LookAheadBufLimit) + return 0; + + Debug.Assert(bufStart == LookAheadBufLimit); + Debug.Assert(bufEnd == LookAheadBufLimit); + + Array.Copy(lookAhead, LookAheadBufLimit, lookAhead, 0, LookAheadSize); + bufEnd = Streams.ReadFully(inStr, lookAhead, LookAheadSize, LookAheadBufLimit); + bufStart = 0; + return bufEnd; + } + + public override int ReadByte() + { + if (bufStart < bufEnd) + return lookAhead[bufStart++]; + + if (FillBuffer() < 1) + return -1; + + return lookAhead[bufStart++]; + } + + public override int Read(byte[] buf, int off, int len) + { + int avail = bufEnd - bufStart; + + int pos = off; + while (len > avail) + { + Array.Copy(lookAhead, bufStart, buf, pos, avail); + + bufStart += avail; + pos += avail; + len -= avail; + + if ((avail = FillBuffer()) < 1) + return pos - off; + } + + Array.Copy(lookAhead, bufStart, buf, pos, len); + bufStart += len; + + return pos + len - off;; + } + + internal byte[] GetLookAhead() + { + byte[] temp = new byte[LookAheadSize]; + Array.Copy(lookAhead, bufStart, temp, 0, LookAheadSize); + return temp; + } + } + + internal InputStreamPacket encData; + internal Stream encStream; + internal TruncatedStream truncStream; + + internal PgpEncryptedData( + InputStreamPacket encData) + { + this.encData = encData; + } + + /// Return the raw input stream for the data stream. + public virtual Stream GetInputStream() + { + return encData.GetInputStream(); + } + + /// Return true if the message is integrity protected. + /// True, if there is a modification detection code namespace associated + /// with this stream. + public bool IsIntegrityProtected() + { + return encData is SymmetricEncIntegrityPacket; + } + + /// Note: This can only be called after the message has been read. + /// True, if the message verifies, false otherwise + public bool Verify() + { + if (!IsIntegrityProtected()) + throw new PgpException("data not integrity protected."); + + DigestStream dIn = (DigestStream) encStream; + + // + // make sure we are at the end. + // + while (encStream.ReadByte() >= 0) + { + // do nothing + } + + // + // process the MDC packet + // + byte[] lookAhead = truncStream.GetLookAhead(); + + IDigest hash = dIn.ReadDigest(); + hash.BlockUpdate(lookAhead, 0, 2); + byte[] digest = DigestUtilities.DoFinal(hash); + + byte[] streamDigest = new byte[digest.Length]; + Array.Copy(lookAhead, 2, streamDigest, 0, streamDigest.Length); + + return Arrays.AreEqual(digest, streamDigest); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpEncryptedDataGenerator.cs b/src/core/srcbc/openpgp/PgpEncryptedDataGenerator.cs new file mode 100644 index 0000000..72907f2 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpEncryptedDataGenerator.cs @@ -0,0 +1,495 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Generator for encrypted objects. + public class PgpEncryptedDataGenerator + : IStreamGenerator + { + private BcpgOutputStream pOut; + private CipherStream cOut; + private IBufferedCipher c; + private bool withIntegrityPacket; + private bool oldFormat; + private DigestStream digestOut; + + private abstract class EncMethod + : ContainedPacket + { + protected byte[] sessionInfo; + protected SymmetricKeyAlgorithmTag encAlgorithm; + protected KeyParameter key; + + public abstract void AddSessionInfo(byte[] si, SecureRandom random); + } + + private class PbeMethod + : EncMethod + { + private S2k s2k; + + internal PbeMethod( + SymmetricKeyAlgorithmTag encAlgorithm, + S2k s2k, + KeyParameter key) + { + this.encAlgorithm = encAlgorithm; + this.s2k = s2k; + this.key = key; + } + + public KeyParameter GetKey() + { + return key; + } + + public override void AddSessionInfo( + byte[] si, + SecureRandom random) + { + string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm); + IBufferedCipher c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding"); + + byte[] iv = new byte[c.GetBlockSize()]; + c.Init(true, new ParametersWithRandom(new ParametersWithIV(key, iv), random)); + + this.sessionInfo = c.DoFinal(si, 0, si.Length - 2); + } + + public override void Encode(BcpgOutputStream pOut) + { + SymmetricKeyEncSessionPacket pk = new SymmetricKeyEncSessionPacket( + encAlgorithm, s2k, sessionInfo); + + pOut.WritePacket(pk); + } + } + + private class PubMethod + : EncMethod + { + internal PgpPublicKey pubKey; + internal BigInteger[] data; + + internal PubMethod( + PgpPublicKey pubKey) + { + this.pubKey = pubKey; + } + + public override void AddSessionInfo( + byte[] si, + SecureRandom random) + { + IBufferedCipher c; + + switch (pubKey.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + c = CipherUtilities.GetCipher("RSA//PKCS1Padding"); + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + c = CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding"); + break; + case PublicKeyAlgorithmTag.Dsa: + throw new PgpException("Can't use DSA for encryption."); + case PublicKeyAlgorithmTag.ECDsa: + throw new PgpException("Can't use ECDSA for encryption."); + default: + throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm); + } + + AsymmetricKeyParameter akp = pubKey.GetKey(); + + c.Init(true, new ParametersWithRandom(akp, random)); + + byte[] encKey = c.DoFinal(si); + + switch (pubKey.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + data = new BigInteger[]{ new BigInteger(1, encKey) }; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + int halfLength = encKey.Length / 2; + data = new BigInteger[] + { + new BigInteger(1, encKey, 0, halfLength), + new BigInteger(1, encKey, halfLength, halfLength) + }; + break; + default: + throw new PgpException("unknown asymmetric algorithm: " + encAlgorithm); + } + } + + public override void Encode(BcpgOutputStream pOut) + { + PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket( + pubKey.KeyId, pubKey.Algorithm, data); + + pOut.WritePacket(pk); + } + } + + private readonly ArrayList methods = new ArrayList(); + private readonly SymmetricKeyAlgorithmTag defAlgorithm; + private readonly SecureRandom rand; + + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm) + { + this.defAlgorithm = encAlgorithm; + this.rand = new SecureRandom(); + } + + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm, + bool withIntegrityPacket) + { + this.defAlgorithm = encAlgorithm; + this.withIntegrityPacket = withIntegrityPacket; + this.rand = new SecureRandom(); + } + + /// Existing SecureRandom constructor. + /// The symmetric algorithm to use. + /// Source of randomness. + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm, + SecureRandom rand) + { + this.defAlgorithm = encAlgorithm; + this.rand = rand; + } + + /// Creates a cipher stream which will have an integrity packet associated with it. + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm, + bool withIntegrityPacket, + SecureRandom rand) + { + this.defAlgorithm = encAlgorithm; + this.rand = rand; + this.withIntegrityPacket = withIntegrityPacket; + } + + /// Base constructor. + /// The symmetric algorithm to use. + /// Source of randomness. + /// PGP 2.6.x compatibility required. + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm, + SecureRandom rand, + bool oldFormat) + { + this.defAlgorithm = encAlgorithm; + this.rand = rand; + this.oldFormat = oldFormat; + } + + /// Add a PBE encryption method to the encrypted object. + public void AddMethod( + char[] passPhrase) + { + byte[] iv = new byte[8]; + rand.NextBytes(iv); + + S2k s2k = new S2k(HashAlgorithmTag.Sha1, iv, 0x60); + + methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.MakeKeyFromPassPhrase(defAlgorithm, s2k, passPhrase))); + } + + /// Add a public key encrypted session key to the encrypted object. + public void AddMethod( + PgpPublicKey key) + { + if (!key.IsEncryptionKey) + { + throw new ArgumentException("passed in key not an encryption key!"); + } + + methods.Add(new PubMethod(key)); + } + + private void AddCheckSum( + byte[] sessionInfo) + { + Debug.Assert(sessionInfo != null); + Debug.Assert(sessionInfo.Length >= 3); + + int check = 0; + + for (int i = 1; i < sessionInfo.Length - 2; i++) + { + check += sessionInfo[i]; + } + + sessionInfo[sessionInfo.Length - 2] = (byte)(check >> 8); + sessionInfo[sessionInfo.Length - 1] = (byte)(check); + } + + private byte[] CreateSessionInfo( + SymmetricKeyAlgorithmTag algorithm, + KeyParameter key) + { + byte[] keyBytes = key.GetKey(); + byte[] sessionInfo = new byte[keyBytes.Length + 3]; + sessionInfo[0] = (byte) algorithm; + keyBytes.CopyTo(sessionInfo, 1); + AddCheckSum(sessionInfo); + return sessionInfo; + } + + /// + ///

    + /// If buffer is non null stream assumed to be partial, otherwise the length will be used + /// to output a fixed length packet. + ///

    + ///

    + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter outStr. + ///

    + ///
    + private Stream Open( + Stream outStr, + long length, + byte[] buffer) + { + if (cOut != null) + throw new InvalidOperationException("generator already in open state"); + if (methods.Count == 0) + throw new InvalidOperationException("No encryption methods specified"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + + pOut = new BcpgOutputStream(outStr); + + KeyParameter key; + + if (methods.Count == 1) + { + if (methods[0] is PbeMethod) + { + PbeMethod m = (PbeMethod)methods[0]; + + key = m.GetKey(); + } + else + { + key = PgpUtilities.MakeRandomKey(defAlgorithm, rand); + + byte[] sessionInfo = CreateSessionInfo(defAlgorithm, key); + PubMethod m = (PubMethod)methods[0]; + + try + { + m.AddSessionInfo(sessionInfo, rand); + } + catch (Exception e) + { + throw new PgpException("exception encrypting session key", e); + } + } + + pOut.WritePacket((ContainedPacket)methods[0]); + } + else // multiple methods + { + key = PgpUtilities.MakeRandomKey(defAlgorithm, rand); + byte[] sessionInfo = CreateSessionInfo(defAlgorithm, key); + + for (int i = 0; i != methods.Count; i++) + { + EncMethod m = (EncMethod)methods[i]; + + try + { + m.AddSessionInfo(sessionInfo, rand); + } + catch (Exception e) + { + throw new PgpException("exception encrypting session key", e); + } + + pOut.WritePacket(m); + } + } + + string cName = PgpUtilities.GetSymmetricCipherName(defAlgorithm); + if (cName == null) + { + throw new PgpException("null cipher specified"); + } + + try + { + if (withIntegrityPacket) + { + cName += "/CFB/NoPadding"; + } + else + { + cName += "/OpenPGPCFB/NoPadding"; + } + + c = CipherUtilities.GetCipher(cName); + + // TODO Confirm the IV should be all zero bytes (not inLineIv - see below) + byte[] iv = new byte[c.GetBlockSize()]; + c.Init(true, new ParametersWithRandom(new ParametersWithIV(key, iv), rand)); + + if (buffer == null) + { + // + // we have to Add block size + 2 for the Generated IV and + 1 + 22 if integrity protected + // + if (withIntegrityPacket) + { + pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricEncryptedIntegrityProtected, length + c.GetBlockSize() + 2 + 1 + 22); + pOut.WriteByte(1); // version number + } + else + { + pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricKeyEncrypted, length + c.GetBlockSize() + 2, oldFormat); + } + } + else + { + if (withIntegrityPacket) + { + pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricEncryptedIntegrityProtected, buffer); + pOut.WriteByte(1); // version number + } + else + { + pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricKeyEncrypted, buffer); + } + } + + int blockSize = c.GetBlockSize(); + byte[] inLineIv = new byte[blockSize + 2]; + rand.NextBytes(inLineIv, 0, blockSize); + Array.Copy(inLineIv, inLineIv.Length - 4, inLineIv, inLineIv.Length - 2, 2); + + Stream myOut = cOut = new CipherStream(pOut, null, c); + + if (withIntegrityPacket) + { + string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1); + IDigest digest = DigestUtilities.GetDigest(digestName); + myOut = digestOut = new DigestStream(myOut, null, digest); + } + + myOut.Write(inLineIv, 0, inLineIv.Length); + + return new WrappedGeneratorStream(this, myOut); + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + } + + /// + ///

    + /// Return an output stream which will encrypt the data as it is written to it. + ///

    + ///

    + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter outStr. + ///

    + ///
    + public Stream Open( + Stream outStr, + long length) + { + return Open(outStr, length, null); + } + + /// + ///

    + /// Return an output stream which will encrypt the data as it is written to it. + /// The stream will be written out in chunks according to the size of the passed in buffer. + ///

    + ///

    + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter outStr. + ///

    + ///

    + /// Note: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used. + ///

    + ///
    + public Stream Open( + Stream outStr, + byte[] buffer) + { + return Open(outStr, 0, buffer); + } + + /// + ///

    + /// Close off the encrypted object - this is equivalent to calling Close() on the stream + /// returned by the Open() method. + ///

    + ///

    + /// Note: This does not close the underlying output stream, only the stream on top of + /// it created by the Open() method. + ///

    + ///
    + public void Close() + { + if (cOut != null) + { + // TODO Should this all be under the try/catch block? + if (digestOut != null) + { + // + // hand code a mod detection packet + // + BcpgOutputStream bOut = new BcpgOutputStream( + digestOut, PacketTag.ModificationDetectionCode, 20); + + bOut.Flush(); + digestOut.Flush(); + + // TODO + byte[] dig = DigestUtilities.DoFinal(digestOut.WriteDigest()); + cOut.Write(dig, 0, dig.Length); + } + + cOut.Flush(); + + try + { + pOut.Write(c.DoFinal()); + pOut.Finish(); + } + catch (Exception e) + { + throw new IOException(e.Message, e); + } + + cOut = null; + pOut = null; + } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpEncryptedDataList.cs b/src/core/srcbc/openpgp/PgpEncryptedDataList.cs new file mode 100644 index 0000000..315e1c5 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpEncryptedDataList.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// A holder for a list of PGP encryption method packets. + public class PgpEncryptedDataList + : PgpObject + { + private ArrayList list = new ArrayList(); + private InputStreamPacket data; + + public PgpEncryptedDataList( + BcpgInputStream bcpgInput) + { + while (bcpgInput.NextPacketTag() == PacketTag.PublicKeyEncryptedSession + || bcpgInput.NextPacketTag() == PacketTag.SymmetricKeyEncryptedSessionKey) + { + list.Add(bcpgInput.ReadPacket()); + } + + data = (InputStreamPacket)bcpgInput.ReadPacket(); + + for (int i = 0; i != list.Count; i++) + { + if (list[i] is SymmetricKeyEncSessionPacket) + { + list[i] = new PgpPbeEncryptedData((SymmetricKeyEncSessionPacket) list[i], data); + } + else + { + list[i] = new PgpPublicKeyEncryptedData((PublicKeyEncSessionPacket) list[i], data); + } + } + } + + public PgpEncryptedData this[int index] + { + get { return (PgpEncryptedData) list[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public object Get(int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return list.Count; } + } + + public int Count + { + get { return list.Count; } + } + + public bool IsEmpty + { + get { return list.Count == 0; } + } + + public IEnumerable GetEncryptedDataObjects() + { + return new EnumerableProxy(list); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpException.cs b/src/core/srcbc/openpgp/PgpException.cs new file mode 100644 index 0000000..067ade9 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Generic exception class for PGP encoding/decoding problems. + public class PgpException + : Exception + { + public PgpException() : base() {} + public PgpException(string message) : base(message) {} + public PgpException(string message, Exception exception) : base(message, exception) {} + + [Obsolete("Use InnerException property")] + public Exception UnderlyingException + { + get { return InnerException; } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpExperimental.cs b/src/core/srcbc/openpgp/PgpExperimental.cs new file mode 100644 index 0000000..8cfac3e --- /dev/null +++ b/src/core/srcbc/openpgp/PgpExperimental.cs @@ -0,0 +1,16 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public class PgpExperimental + : PgpObject + { + private readonly ExperimentalPacket p; + + public PgpExperimental( + BcpgInputStream bcpgIn) + { + p = (ExperimentalPacket) bcpgIn.ReadPacket(); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpKeyFlags.cs b/src/core/srcbc/openpgp/PgpKeyFlags.cs new file mode 100644 index 0000000..49cb7cb --- /dev/null +++ b/src/core/srcbc/openpgp/PgpKeyFlags.cs @@ -0,0 +1,13 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Key flag values for the KeyFlags subpacket. + public abstract class PgpKeyFlags + { + public const int CanCertify = 0x01; // This key may be used to certify other keys. + public const int CanSign = 0x02; // This key may be used to sign data. + public const int CanEncryptCommunications = 0x04; // This key may be used to encrypt communications. + public const int CanEncryptStorage = 0x08; // This key may be used to encrypt storage. + public const int MaybeSplit = 0x10; // The private component of this key may have been split by a secret-sharing mechanism. + public const int MaybeShared = 0x80; // The private component of this key may be in the possession of more than one person. + } +} diff --git a/src/core/srcbc/openpgp/PgpKeyPair.cs b/src/core/srcbc/openpgp/PgpKeyPair.cs new file mode 100644 index 0000000..d4aa9ad --- /dev/null +++ b/src/core/srcbc/openpgp/PgpKeyPair.cs @@ -0,0 +1,67 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// General class to handle JCA key pairs and convert them into OpenPGP ones. + ///

    + /// A word for the unwary, the KeyId for an OpenPGP public key is calculated from + /// a hash that includes the time of creation, if you pass a different date to the + /// constructor below with the same public private key pair the KeyIs will not be the + /// same as for previous generations of the key, so ideally you only want to do + /// this once. + ///

    + ///
    + public class PgpKeyPair + { + private readonly PgpPublicKey pub; + private readonly PgpPrivateKey priv; + + public PgpKeyPair( + PublicKeyAlgorithmTag algorithm, + AsymmetricCipherKeyPair keyPair, + DateTime time) + : this(algorithm, keyPair.Public, keyPair.Private, time) + { + } + + public PgpKeyPair( + PublicKeyAlgorithmTag algorithm, + AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter privKey, + DateTime time) + { + this.pub = new PgpPublicKey(algorithm, pubKey, time); + this.priv = new PgpPrivateKey(privKey, pub.KeyId); + } + + /// Create a key pair from a PgpPrivateKey and a PgpPublicKey. + /// The public key. + /// The private key. + public PgpKeyPair( + PgpPublicKey pub, + PgpPrivateKey priv) + { + this.pub = pub; + this.priv = priv; + } + + /// The keyId associated with this key pair. + public long KeyId + { + get { return pub.KeyId; } + } + + public PgpPublicKey PublicKey + { + get { return pub; } + } + + public PgpPrivateKey PrivateKey + { + get { return priv; } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpKeyRingGenerator.cs b/src/core/srcbc/openpgp/PgpKeyRingGenerator.cs new file mode 100644 index 0000000..a423762 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpKeyRingGenerator.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// Generator for a PGP master and subkey ring. + /// This class will generate both the secret and public key rings + /// + public class PgpKeyRingGenerator + { + private ArrayList keys = new ArrayList(); + private string id; + private SymmetricKeyAlgorithmTag encAlgorithm; + private int certificationLevel; + private char[] passPhrase; + private bool useSha1; + private PgpKeyPair masterKey; + private PgpSignatureSubpacketVector hashedPacketVector; + private PgpSignatureSubpacketVector unhashedPacketVector; + private SecureRandom rand; + + /// + /// Create a new key ring generator using old style checksumming. It is recommended to use + /// SHA1 checksumming where possible. + /// + /// The certification level for keys on this ring. + /// The master key pair. + /// The id to be associated with the ring. + /// The algorithm to be used to protect secret keys. + /// The passPhrase to be used to protect secret keys. + /// Packets to be included in the certification hash. + /// Packets to be attached unhashed to the certification. + /// input secured random. + public PgpKeyRingGenerator( + int certificationLevel, + PgpKeyPair masterKey, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, masterKey, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand) + { + } + + /// + /// Create a new key ring generator. + /// + /// The certification level for keys on this ring. + /// The master key pair. + /// The id to be associated with the ring. + /// The algorithm to be used to protect secret keys. + /// The passPhrase to be used to protect secret keys. + /// Checksum the secret keys with SHA1 rather than the older 16 bit checksum. + /// Packets to be included in the certification hash. + /// Packets to be attached unhashed to the certification. + /// input secured random. + public PgpKeyRingGenerator( + int certificationLevel, + PgpKeyPair masterKey, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + { + this.certificationLevel = certificationLevel; + this.masterKey = masterKey; + this.id = id; + this.encAlgorithm = encAlgorithm; + this.passPhrase = passPhrase; + this.useSha1 = useSha1; + this.hashedPacketVector = hashedPackets; + this.unhashedPacketVector = unhashedPackets; + this.rand = rand; + + keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand)); + } + + /// Add a subkey to the key ring to be generated with default certification. + public void AddSubKey( + PgpKeyPair keyPair) + { + AddSubKey(keyPair, this.hashedPacketVector, this.unhashedPacketVector); + } + + /// + /// Add a subkey with specific hashed and unhashed packets associated with it and + /// default certification. + /// + /// Public/private key pair. + /// Hashed packet values to be included in certification. + /// Unhashed packets values to be included in certification. + /// + public void AddSubKey( + PgpKeyPair keyPair, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets) + { + try + { + PgpSignatureGenerator sGen = new PgpSignatureGenerator( + masterKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1); + + // + // Generate the certification + // + sGen.InitSign(PgpSignature.SubkeyBinding, masterKey.PrivateKey); + + sGen.SetHashedSubpackets(hashedPackets); + sGen.SetUnhashedSubpackets(unhashedPackets); + + ArrayList subSigs = new ArrayList(); + + subSigs.Add(sGen.GenerateCertification(masterKey.PublicKey, keyPair.PublicKey)); + + keys.Add(new PgpSecretKey(keyPair, null, subSigs, encAlgorithm, passPhrase, useSha1, rand)); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("exception adding subkey: ", e); + } + } + + /// Return the secret key ring. + public PgpSecretKeyRing GenerateSecretKeyRing() + { + return new PgpSecretKeyRing(keys); + } + + /// Return the public key ring that corresponds to the secret key ring. + public PgpPublicKeyRing GeneratePublicKeyRing() + { + ArrayList pubKeys = new ArrayList(); + + IEnumerator enumerator = keys.GetEnumerator(); + enumerator.MoveNext(); + + PgpSecretKey pgpSecretKey = (PgpSecretKey) enumerator.Current; + pubKeys.Add(pgpSecretKey.PublicKey); + + while (enumerator.MoveNext()) + { + pgpSecretKey = (PgpSecretKey) enumerator.Current; + + PgpPublicKey k = new PgpPublicKey(pgpSecretKey.PublicKey); + k.publicPk = new PublicSubkeyPacket( + k.Algorithm, k.CreationTime, k.publicPk.Key); + + pubKeys.Add(k); + } + + return new PgpPublicKeyRing(pubKeys); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpKeyValidationException.cs b/src/core/srcbc/openpgp/PgpKeyValidationException.cs new file mode 100644 index 0000000..81bb818 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpKeyValidationException.cs @@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// Thrown if the key checksum is invalid. + /// + public class PgpKeyValidationException + : PgpException + { + public PgpKeyValidationException() : base() {} + public PgpKeyValidationException(string message) : base(message) {} + public PgpKeyValidationException(string message, Exception exception) : base(message, exception) {} + } +} diff --git a/src/core/srcbc/openpgp/PgpLiteralData.cs b/src/core/srcbc/openpgp/PgpLiteralData.cs new file mode 100644 index 0000000..10b7fc0 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpLiteralData.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Class for processing literal data objects. + public class PgpLiteralData + : PgpObject + { + public const char Binary = 'b'; + public const char Text = 't'; + + /// The special name indicating a "for your eyes only" packet. + public const string Console = "_CONSOLE"; + + private LiteralDataPacket data; + + public PgpLiteralData( + BcpgInputStream bcpgInput) + { + data = (LiteralDataPacket) bcpgInput.ReadPacket(); + } + + /// The format of the data stream - Binary or Text + public int Format + { + get { return data.Format; } + } + + /// The file name that's associated with the data stream. + public string FileName + { + get { return data.FileName; } + } + + /// The modification time for the file. + public DateTime ModificationTime + { + get { return DateTimeUtilities.UnixMsToDateTime(data.ModificationTime); } + } + + /// The raw input stream for the data stream. + public Stream GetInputStream() + { + return data.GetInputStream(); + } + + /// The input stream representing the data stream. + public Stream GetDataStream() + { + return GetInputStream(); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpLiteralDataGenerator.cs b/src/core/srcbc/openpgp/PgpLiteralDataGenerator.cs new file mode 100644 index 0000000..e00394d --- /dev/null +++ b/src/core/srcbc/openpgp/PgpLiteralDataGenerator.cs @@ -0,0 +1,177 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Class for producing literal data packets. + public class PgpLiteralDataGenerator + : IStreamGenerator + { + public const char Binary = PgpLiteralData.Binary; + public const char Text = PgpLiteralData.Text; + + /// The special name indicating a "for your eyes only" packet. + public const string Console = PgpLiteralData.Console; + + private BcpgOutputStream pkOut; + private bool oldFormat; + + public PgpLiteralDataGenerator() + { + } + + /// + /// Generates literal data objects in the old format. + /// This is important if you need compatibility with PGP 2.6.x. + /// + /// If true, uses old format. + public PgpLiteralDataGenerator( + bool oldFormat) + { + this.oldFormat = oldFormat; + } + + private void WriteHeader( + BcpgOutputStream outStr, + char format, + string name, + long modificationTime) + { + byte[] asciiName = Strings.ToByteArray(name); + + outStr.Write( + (byte) format, + (byte) asciiName.Length); + + outStr.Write(asciiName); + + long modDate = modificationTime / 1000L; + + outStr.Write( + (byte)(modDate >> 24), + (byte)(modDate >> 16), + (byte)(modDate >> 8), + (byte)modDate); + } + + /// + ///

    + /// Open a literal data packet, returning a stream to store the data inside the packet. + ///

    + ///

    + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter outStr. + ///

    + ///
    + /// The stream we want the packet in. + /// The format we are using. + /// The name of the 'file'. + /// The length of the data we will write. + /// The time of last modification we want stored. + public Stream Open( + Stream outStr, + char format, + string name, + long length, + DateTime modificationTime) + { + if (pkOut != null) + throw new InvalidOperationException("generator already in open state"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + + // Do this first, since it might throw an exception + long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime); + + pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData, + length + 2 + name.Length + 4, oldFormat); + + WriteHeader(pkOut, format, name, unixMs); + + return new WrappedGeneratorStream(this, pkOut); + } + + /// + ///

    + /// Open a literal data packet, returning a stream to store the data inside the packet, + /// as an indefinite length stream. The stream is written out as a series of partial + /// packets with a chunk size determined by the size of the passed in buffer. + ///

    + ///

    + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter outStr. + ///

    + ///

    + /// Note: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used.

    + ///
    + /// The stream we want the packet in. + /// The format we are using. + /// The name of the 'file'. + /// The time of last modification we want stored. + /// The buffer to use for collecting data to put into chunks. + public Stream Open( + Stream outStr, + char format, + string name, + DateTime modificationTime, + byte[] buffer) + { + if (pkOut != null) + throw new InvalidOperationException("generator already in open state"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + + // Do this first, since it might throw an exception + long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime); + + pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData, buffer); + + WriteHeader(pkOut, format, name, unixMs); + + return new WrappedGeneratorStream(this, pkOut); + } + + /// + ///

    + /// Open a literal data packet for the passed in FileInfo object, returning + /// an output stream for saving the file contents. + ///

    + ///

    + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter outStr. + ///

    + ///
    + /// The stream we want the packet in. + /// The format we are using. + /// The FileInfo object containg the packet details. + public Stream Open( + Stream outStr, + char format, + FileInfo file) + { + return Open(outStr, format, file.Name, file.Length, file.LastWriteTime); + } + + /// + /// Close the literal data packet - this is equivalent to calling Close() + /// on the stream returned by the Open() method. + /// + public void Close() + { + if (pkOut != null) + { + pkOut.Finish(); + pkOut.Flush(); + pkOut = null; + } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpMarker.cs b/src/core/srcbc/openpgp/PgpMarker.cs new file mode 100644 index 0000000..3e8f27f --- /dev/null +++ b/src/core/srcbc/openpgp/PgpMarker.cs @@ -0,0 +1,18 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// A PGP marker packet - in general these should be ignored other than where + /// the idea is to preserve the original input stream. + /// + public class PgpMarker + : PgpObject + { + private readonly MarkerPacket p; + + public PgpMarker( + BcpgInputStream bcpgIn) + { + p = (MarkerPacket) bcpgIn.ReadPacket(); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpObjectFactory.cs b/src/core/srcbc/openpgp/PgpObjectFactory.cs new file mode 100644 index 0000000..ae23754 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpObjectFactory.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// General class for reading a PGP object stream. + ///

    + /// Note: if this class finds a PgpPublicKey or a PgpSecretKey it + /// will create a PgpPublicKeyRing, or a PgpSecretKeyRing for each + /// key found. If all you are trying to do is read a key ring file use + /// either PgpPublicKeyRingBundle or PgpSecretKeyRingBundle.

    + ///
    + public class PgpObjectFactory + { + private readonly BcpgInputStream bcpgIn; + + public PgpObjectFactory( + Stream inputStream) + { + this.bcpgIn = BcpgInputStream.Wrap(inputStream); + } + + public PgpObjectFactory( + byte[] bytes) + : this(new MemoryStream(bytes, false)) + { + } + + /// Return the next object in the stream, or null if the end is reached. + /// On a parse error + public PgpObject NextPgpObject() + { + PacketTag tag = bcpgIn.NextPacketTag(); + + if ((int) tag == -1) return null; + + switch (tag) + { + case PacketTag.Signature: + { + ArrayList l = new ArrayList(); + + while (bcpgIn.NextPacketTag() == PacketTag.Signature) + { + try + { + l.Add(new PgpSignature(bcpgIn)); + } + catch (PgpException e) + { + throw new IOException("can't create signature object: " + e); + } + } + + return new PgpSignatureList( + (PgpSignature[]) l.ToArray(typeof(PgpSignature))); + } + case PacketTag.SecretKey: + try + { + return new PgpSecretKeyRing(bcpgIn); + } + catch (PgpException e) + { + throw new IOException("can't create secret key object: " + e); + } + case PacketTag.PublicKey: + return new PgpPublicKeyRing(bcpgIn); + case PacketTag.CompressedData: + return new PgpCompressedData(bcpgIn); + case PacketTag.LiteralData: + return new PgpLiteralData(bcpgIn); + case PacketTag.PublicKeyEncryptedSession: + case PacketTag.SymmetricKeyEncryptedSessionKey: + return new PgpEncryptedDataList(bcpgIn); + case PacketTag.OnePassSignature: + { + ArrayList l = new ArrayList(); + + while (bcpgIn.NextPacketTag() == PacketTag.OnePassSignature) + { + try + { + l.Add(new PgpOnePassSignature(bcpgIn)); + } + catch (PgpException e) + { + throw new IOException("can't create one pass signature object: " + e); + } + } + + return new PgpOnePassSignatureList( + (PgpOnePassSignature[]) l.ToArray(typeof(PgpOnePassSignature))); + } + case PacketTag.Marker: + return new PgpMarker(bcpgIn); + case PacketTag.Experimental1: + case PacketTag.Experimental2: + case PacketTag.Experimental3: + case PacketTag.Experimental4: + return new PgpExperimental(bcpgIn); + } + + throw new IOException("unknown object in stream " + bcpgIn.NextPacketTag()); + } + + [Obsolete("Use NextPgpObject() instead")] + public object NextObject() + { + return NextPgpObject(); + } + + /// + /// Return all available objects in a list. + /// + /// An IList containing all objects from this factory, in order. + public IList AllPgpObjects() + { + ArrayList result = new ArrayList(); + PgpObject pgpObject; + while ((pgpObject = NextPgpObject()) != null) + { + result.Add(pgpObject); + } + return result; + } + } +} diff --git a/src/core/srcbc/openpgp/PgpOnePassSignature.cs b/src/core/srcbc/openpgp/PgpOnePassSignature.cs new file mode 100644 index 0000000..c8c4128 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpOnePassSignature.cs @@ -0,0 +1,179 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// A one pass signature object. + public class PgpOnePassSignature + { + private OnePassSignaturePacket sigPack; + private int signatureType; + private ISigner sig; + private byte lastb; + + internal PgpOnePassSignature( + BcpgInputStream bcpgInput) + : this((OnePassSignaturePacket) bcpgInput.ReadPacket()) + { + } + + internal PgpOnePassSignature( + OnePassSignaturePacket sigPack) + { + this.sigPack = sigPack; + this.signatureType = sigPack.SignatureType; + + try + { + this.sig = SignerUtilities.GetSigner( + PgpUtilities.GetSignatureName(sigPack.KeyAlgorithm, sigPack.HashAlgorithm)); + } + catch (Exception e) + { + throw new PgpException("can't set up signature object.", e); + } + } + + /// Initialise the signature object for verification. + public void InitVerify( + PgpPublicKey pubKey) + { + lastb = 0; + + try + { + sig.Init(false, pubKey.GetKey()); + } + catch (InvalidKeyException e) + { + throw new PgpException("invalid key.", e); + } + } + + public void Update( + byte b) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + doCanonicalUpdateByte(b); + } + else + { + sig.Update(b); + } + } + + private void doCanonicalUpdateByte( + byte b) + { + if (b == '\r') + { + doUpdateCRLF(); + } + else if (b == '\n') + { + if (lastb != '\r') + { + doUpdateCRLF(); + } + } + else + { + sig.Update(b); + } + + lastb = b; + } + + private void doUpdateCRLF() + { + sig.Update((byte)'\r'); + sig.Update((byte)'\n'); + } + + public void Update( + byte[] bytes) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + for (int i = 0; i != bytes.Length; i++) + { + doCanonicalUpdateByte(bytes[i]); + } + } + else + { + sig.BlockUpdate(bytes, 0, bytes.Length); + } + } + + public void Update( + byte[] bytes, + int off, + int length) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + int finish = off + length; + + for (int i = off; i != finish; i++) + { + doCanonicalUpdateByte(bytes[i]); + } + } + else + { + sig.BlockUpdate(bytes, off, length); + } + } + + /// Verify the calculated signature against the passed in PgpSignature. + public bool Verify( + PgpSignature pgpSig) + { + byte[] trailer = pgpSig.GetSignatureTrailer(); + + sig.BlockUpdate(trailer, 0, trailer.Length); + + return sig.VerifySignature(pgpSig.GetSignature()); + } + + public long KeyId + { + get { return sigPack.KeyId; } + } + + public int SignatureType + { + get { return sigPack.SignatureType; } + } + + public HashAlgorithmTag HashAlgorithm + { + get { return sigPack.HashAlgorithm; } + } + + public PublicKeyAlgorithmTag KeyAlgorithm + { + get { return sigPack.KeyAlgorithm; } + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream.Wrap(outStr).WritePacket(sigPack); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpOnePassSignatureList.cs b/src/core/srcbc/openpgp/PgpOnePassSignatureList.cs new file mode 100644 index 0000000..6f15c86 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpOnePassSignatureList.cs @@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Holder for a list of PgpOnePassSignature objects. + public class PgpOnePassSignatureList + : PgpObject + { + private readonly PgpOnePassSignature[] sigs; + + public PgpOnePassSignatureList( + PgpOnePassSignature[] sigs) + { + this.sigs = (PgpOnePassSignature[]) sigs.Clone(); + } + + public PgpOnePassSignatureList( + PgpOnePassSignature sig) + { + this.sigs = new PgpOnePassSignature[]{ sig }; + } + + public PgpOnePassSignature this[int index] + { + get { return sigs[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public PgpOnePassSignature Get( + int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return sigs.Length; } + } + + public int Count + { + get { return sigs.Length; } + } + + public bool IsEmpty + { + get { return (sigs.Length == 0); } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpPbeEncryptedData.cs b/src/core/srcbc/openpgp/PgpPbeEncryptedData.cs new file mode 100644 index 0000000..66cbec4 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpPbeEncryptedData.cs @@ -0,0 +1,135 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// A password based encryption object. + public class PgpPbeEncryptedData + : PgpEncryptedData + { + private readonly SymmetricKeyEncSessionPacket keyData; + + internal PgpPbeEncryptedData( + SymmetricKeyEncSessionPacket keyData, + InputStreamPacket encData) + : base(encData) + { + this.keyData = keyData; + } + + /// Return the raw input stream for the data stream. + public override Stream GetInputStream() + { + return encData.GetInputStream(); + } + + /// Return the decrypted input stream, using the passed in passphrase. + public Stream GetDataStream( + char[] passPhrase) + { + try + { + SymmetricKeyAlgorithmTag keyAlgorithm = keyData.EncAlgorithm; + + KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase( + keyAlgorithm, keyData.S2k, passPhrase); + + + byte[] secKeyData = keyData.GetSecKeyData(); + if (secKeyData != null && secKeyData.Length > 0) + { + IBufferedCipher keyCipher = CipherUtilities.GetCipher( + PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + "/CFB/NoPadding"); + + keyCipher.Init(false, + new ParametersWithIV(key, new byte[keyCipher.GetBlockSize()])); + + byte[] keyBytes = keyCipher.DoFinal(secKeyData); + + keyAlgorithm = (SymmetricKeyAlgorithmTag) keyBytes[0]; + + key = ParameterUtilities.CreateKeyParameter( + PgpUtilities.GetSymmetricCipherName(keyAlgorithm), + keyBytes, 1, keyBytes.Length - 1); + } + + + IBufferedCipher c = CreateStreamCipher(keyAlgorithm); + + byte[] iv = new byte[c.GetBlockSize()]; + + c.Init(false, new ParametersWithIV(key, iv)); + + encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c, null)); + + if (encData is SymmetricEncIntegrityPacket) + { + truncStream = new TruncatedStream(encStream); + + string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1); + IDigest digest = DigestUtilities.GetDigest(digestName); + + encStream = new DigestStream(truncStream, digest, null); + } + + if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length) + throw new EndOfStreamException("unexpected end of stream."); + + int v1 = encStream.ReadByte(); + int v2 = encStream.ReadByte(); + + if (v1 < 0 || v2 < 0) + throw new EndOfStreamException("unexpected end of stream."); + + + // Note: the oracle attack on the "quick check" bytes is not deemed + // a security risk for PBE (see PgpPublicKeyEncryptedData) + + bool repeatCheckPassed = + iv[iv.Length - 2] == (byte)v1 + && iv[iv.Length - 1] == (byte)v2; + + // Note: some versions of PGP appear to produce 0 for the extra + // bytes rather than repeating the two previous bytes + bool zeroesCheckPassed = + v1 == 0 + && v2 == 0; + + if (!repeatCheckPassed && !zeroesCheckPassed) + { + throw new PgpDataValidationException("quick check failed."); + } + + + return encStream; + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + } + + private IBufferedCipher CreateStreamCipher( + SymmetricKeyAlgorithmTag keyAlgorithm) + { + string mode = (encData is SymmetricEncIntegrityPacket) + ? "CFB" + : "OpenPGPCFB"; + + string cName = PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + + "/" + mode + "/NoPadding"; + + return CipherUtilities.GetCipher(cName); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpPrivateKey.cs b/src/core/srcbc/openpgp/PgpPrivateKey.cs new file mode 100644 index 0000000..e4f5853 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpPrivateKey.cs @@ -0,0 +1,42 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// General class to contain a private key for use with other OpenPGP objects. + public class PgpPrivateKey + { + private readonly long keyId; + private readonly AsymmetricKeyParameter privateKey; + + /// + /// Create a PgpPrivateKey from a regular private key and the ID of its + /// associated public key. + /// + /// Private key to use. + /// ID of the corresponding public key. + public PgpPrivateKey( + AsymmetricKeyParameter privateKey, + long keyId) + { + if (!privateKey.IsPrivate) + throw new ArgumentException("Expected a private key", "privateKey"); + + this.privateKey = privateKey; + this.keyId = keyId; + } + + /// The keyId associated with the contained private key. + public long KeyId + { + get { return keyId; } + } + + /// The contained private key. + public AsymmetricKeyParameter Key + { + get { return privateKey; } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpPublicKey.cs b/src/core/srcbc/openpgp/PgpPublicKey.cs new file mode 100644 index 0000000..131ba28 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpPublicKey.cs @@ -0,0 +1,835 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// General class to handle a PGP public key object. + public class PgpPublicKey + { + private static readonly int[] MasterKeyCertificationTypes = new int[] + { + PgpSignature.PositiveCertification, + PgpSignature.CasualCertification, + PgpSignature.NoCertification, + PgpSignature.DefaultCertification + }; + + private long keyId; + private byte[] fingerprint; + private int keyStrength; + + internal PublicKeyPacket publicPk; + private TrustPacket trustPk; + private ArrayList keySigs = new ArrayList(); + private ArrayList ids = new ArrayList(); + private ArrayList idTrusts = new ArrayList(); + private ArrayList idSigs = new ArrayList(); + private ArrayList subSigs; + + private void Init() + { + IBcpgKey key = publicPk.Key; + + if (publicPk.Version <= 3) + { + RsaPublicBcpgKey rK = (RsaPublicBcpgKey) key; + + this.keyId = rK.Modulus.LongValue; + + try + { + IDigest digest = DigestUtilities.GetDigest("MD5"); + + byte[] bytes = rK.Modulus.ToByteArrayUnsigned(); + digest.BlockUpdate(bytes, 0, bytes.Length); + + bytes = rK.PublicExponent.ToByteArrayUnsigned(); + digest.BlockUpdate(bytes, 0, bytes.Length); + + this.fingerprint = DigestUtilities.DoFinal(digest); + } + //catch (NoSuchAlgorithmException) + catch (Exception) + { + throw new IOException("can't find MD5"); + } + + this.keyStrength = rK.Modulus.BitLength; + } + else + { + byte[] kBytes = publicPk.GetEncodedContents(); + + try + { + IDigest digest = DigestUtilities.GetDigest("SHA1"); + + digest.Update(0x99); + digest.Update((byte)(kBytes.Length >> 8)); + digest.Update((byte)kBytes.Length); + digest.BlockUpdate(kBytes, 0, kBytes.Length); + this.fingerprint = DigestUtilities.DoFinal(digest); + } + //catch (NoSuchAlgorithmException) + catch (Exception) + { + throw new IOException("can't find SHA1"); + } + + this.keyId = (long)(((ulong)fingerprint[fingerprint.Length - 8] << 56) + | ((ulong)fingerprint[fingerprint.Length - 7] << 48) + | ((ulong)fingerprint[fingerprint.Length - 6] << 40) + | ((ulong)fingerprint[fingerprint.Length - 5] << 32) + | ((ulong)fingerprint[fingerprint.Length - 4] << 24) + | ((ulong)fingerprint[fingerprint.Length - 3] << 16) + | ((ulong)fingerprint[fingerprint.Length - 2] << 8) + | (ulong)fingerprint[fingerprint.Length - 1]); + + if (key is RsaPublicBcpgKey) + { + this.keyStrength = ((RsaPublicBcpgKey)key).Modulus.BitLength; + } + else if (key is DsaPublicBcpgKey) + { + this.keyStrength = ((DsaPublicBcpgKey)key).P.BitLength; + } + else if (key is ElGamalPublicBcpgKey) + { + this.keyStrength = ((ElGamalPublicBcpgKey)key).P.BitLength; + } + } + } + + /// + /// Create a PgpPublicKey from the passed in lightweight one. + /// + /// + /// Note: the time passed in affects the value of the key's keyId, so you probably only want + /// to do this once for a lightweight key, or make sure you keep track of the time you used. + /// + /// Asymmetric algorithm type representing the public key. + /// Actual public key to associate. + /// Date of creation. + /// If pubKey is not public. + /// On key creation problem. + public PgpPublicKey( + PublicKeyAlgorithmTag algorithm, + AsymmetricKeyParameter pubKey, + DateTime time) + { + if (pubKey.IsPrivate) + throw new ArgumentException("Expected a public key", "pubKey"); + + IBcpgKey bcpgKey; + if (pubKey is RsaKeyParameters) + { + RsaKeyParameters rK = (RsaKeyParameters) pubKey; + + bcpgKey = new RsaPublicBcpgKey(rK.Modulus, rK.Exponent); + } + else if (pubKey is DsaPublicKeyParameters) + { + DsaPublicKeyParameters dK = (DsaPublicKeyParameters) pubKey; + DsaParameters dP = dK.Parameters; + + bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y); + } + else if (pubKey is ElGamalPublicKeyParameters) + { + ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey; + ElGamalParameters eS = eK.Parameters; + + bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y); + } + else + { + throw new PgpException("unknown key class"); + } + + this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey); + this.ids = new ArrayList(); + this.idSigs = new ArrayList(); + + try + { + Init(); + } + catch (IOException e) + { + throw new PgpException("exception calculating keyId", e); + } + } + + /// Constructor for a sub-key. + internal PgpPublicKey( + PublicKeyPacket publicPk, + TrustPacket trustPk, + ArrayList sigs) + { + this.publicPk = publicPk; + this.trustPk = trustPk; + this.subSigs = sigs; + + Init(); + } + + internal PgpPublicKey( + PgpPublicKey key, + TrustPacket trust, + ArrayList subSigs) + { + this.publicPk = key.publicPk; + this.trustPk = trust; + this.subSigs = subSigs; + + this.fingerprint = key.fingerprint; + this.keyId = key.keyId; + this.keyStrength = key.keyStrength; + } + + /// Copy constructor. + /// The public key to copy. + internal PgpPublicKey( + PgpPublicKey pubKey) + { + this.publicPk = pubKey.publicPk; + + this.keySigs = new ArrayList(pubKey.keySigs); + this.ids = new ArrayList(pubKey.ids); + this.idTrusts = new ArrayList(pubKey.idTrusts); + this.idSigs = new ArrayList(pubKey.idSigs.Count); + for (int i = 0; i != pubKey.idSigs.Count; i++) + { + this.idSigs.Add(new ArrayList((ArrayList)pubKey.idSigs[i])); + } + + if (pubKey.subSigs != null) + { + this.subSigs = new ArrayList(pubKey.subSigs.Count); + for (int i = 0; i != pubKey.subSigs.Count; i++) + { + this.subSigs.Add(pubKey.subSigs[i]); + } + } + + this.fingerprint = pubKey.fingerprint; + this.keyId = pubKey.keyId; + this.keyStrength = pubKey.keyStrength; + } + + internal PgpPublicKey( + PublicKeyPacket publicPk, + TrustPacket trustPk, + ArrayList keySigs, + ArrayList ids, + ArrayList idTrusts, + ArrayList idSigs) + { + this.publicPk = publicPk; + this.trustPk = trustPk; + this.keySigs = keySigs; + this.ids = ids; + this.idTrusts = idTrusts; + this.idSigs = idSigs; + + Init(); + } + + internal PgpPublicKey( + PublicKeyPacket publicPk, + ArrayList ids, + ArrayList idSigs) + { + this.publicPk = publicPk; + this.ids = ids; + this.idSigs = idSigs; + Init(); + } + + /// The version of this key. + public int Version + { + get { return publicPk.Version; } + } + + /// The creation time of this key. + public DateTime CreationTime + { + get { return publicPk.GetTime(); } + } + + /// The number of valid days from creation time - zero means no expiry. + public int ValidDays + { + get + { + if (publicPk.Version > 3) + { + return (int)(GetValidSeconds() / (24 * 60 * 60)); + } + + return publicPk.ValidDays; + } + } + + /// Return the trust data associated with the public key, if present. + /// A byte array with trust data, null otherwise. + public byte[] GetTrustData() + { + if (trustPk == null) + { + return null; + } + + return trustPk.GetLevelAndTrustAmount(); + } + + /// The number of valid seconds from creation time - zero means no expiry. + public long GetValidSeconds() + { + if (publicPk.Version > 3) + { + if (IsMasterKey) + { + for (int i = 0; i != MasterKeyCertificationTypes.Length; i++) + { + long seconds = GetExpirationTimeFromSig(true, MasterKeyCertificationTypes[i]); + + if (seconds >= 0) + { + return seconds; + } + } + } + else + { + long seconds = GetExpirationTimeFromSig(false, PgpSignature.SubkeyBinding); + + if (seconds >= 0) + { + return seconds; + } + } + + return 0; + } + + return (long) publicPk.ValidDays * 24 * 60 * 60; + } + + private long GetExpirationTimeFromSig( + bool selfSigned, + int signatureType) + { + foreach (PgpSignature sig in GetSignaturesOfType(signatureType)) + { + if (!selfSigned || sig.KeyId == KeyId) + { + PgpSignatureSubpacketVector hashed = sig.GetHashedSubPackets(); + + if (hashed != null) + { + return hashed.GetKeyExpirationTime(); + } + + return 0; + } + } + + return -1; + } + + /// The keyId associated with the public key. + public long KeyId + { + get { return keyId; } + } + + /// The fingerprint of the key + public byte[] GetFingerprint() + { + return (byte[]) fingerprint.Clone(); + } + + + /// True, if this key is marked as suitable for encryption + /// True, if this key is marked as suitable for using for encryption. + public bool IsEncryptionKey + { + get + { + switch (publicPk.Algorithm) + { + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + return true; + default: + return false; + } + } + } + + /// True, if this is a master key. + public bool IsMasterKey + { + get { return subSigs == null; } + } + + /// The algorithm code associated with the public key. + public PublicKeyAlgorithmTag Algorithm + { + get { return publicPk.Algorithm; } + } + + /// The strength of the key in bits. + public int BitStrength + { + get { return keyStrength; } + } + + /// The public key contained in the object. + /// A lightweight public key. + /// If the key algorithm is not recognised. + public AsymmetricKeyParameter GetKey() + { + try + { + switch (publicPk.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey) publicPk.Key; + return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent); + case PublicKeyAlgorithmTag.Dsa: + DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey) publicPk.Key; + return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G)); + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey) publicPk.Key; + return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G)); + default: + throw new PgpException("unknown public key algorithm encountered"); + } + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("exception constructing public key", e); + } + } + + /// Allows enumeration of any user IDs associated with the key. + /// An IEnumerable of string objects. + public IEnumerable GetUserIds() + { + ArrayList temp = new ArrayList(); + + foreach (object o in ids) + { + if (o is string) + { + temp.Add(o); + } + } + + return new EnumerableProxy(temp); + } + + /// Allows enumeration of any user attribute vectors associated with the key. + /// An IEnumerable of PgpUserAttributeSubpacketVector objects. + public IEnumerable GetUserAttributes() + { + ArrayList temp = new ArrayList(); + + foreach (object o in ids) + { + if (o is PgpUserAttributeSubpacketVector) + { + temp.Add(o); + } + } + + return new EnumerableProxy(temp); + } + + /// Allows enumeration of any signatures associated with the passed in id. + /// The ID to be matched. + /// An IEnumerable of PgpSignature objects. + public IEnumerable GetSignaturesForId( + string id) + { + if (id == null) + throw new ArgumentNullException("id"); + + for (int i = 0; i != ids.Count; i++) + { + if (id.Equals(ids[i])) + { + return new EnumerableProxy((ArrayList) idSigs[i]); + } + } + + return null; + } + + /// Allows enumeration of signatures associated with the passed in user attributes. + /// The vector of user attributes to be matched. + /// An IEnumerable of PgpSignature objects. + public IEnumerable GetSignaturesForUserAttribute( + PgpUserAttributeSubpacketVector userAttributes) + { + for (int i = 0; i != ids.Count; i++) + { + if (userAttributes.Equals(ids[i])) + { + return new EnumerableProxy((ArrayList) idSigs[i]); + } + } + + return null; + } + + /// Allows enumeration of signatures of the passed in type that are on this key. + /// The type of the signature to be returned. + /// An IEnumerable of PgpSignature objects. + public IEnumerable GetSignaturesOfType( + int signatureType) + { + ArrayList temp = new ArrayList(); + + foreach (PgpSignature sig in GetSignatures()) + { + if (sig.SignatureType == signatureType) + { + temp.Add(sig); + } + } + + return new EnumerableProxy(temp); + } + + /// Allows enumeration of all signatures/certifications associated with this key. + /// An IEnumerable with all signatures/certifications. + public IEnumerable GetSignatures() + { + ArrayList sigs; + if (subSigs != null) + { + sigs = subSigs; + } + else + { + sigs = new ArrayList(keySigs); + + foreach (ICollection extraSigs in idSigs) + { + sigs.AddRange(extraSigs); + } + } + + return new EnumerableProxy(sigs); + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + Encode(bOut); + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); + + bcpgOut.WritePacket(publicPk); + if (trustPk != null) + { + bcpgOut.WritePacket(trustPk); + } + + if (subSigs == null) // not a sub-key + { + foreach (PgpSignature keySig in keySigs) + { + keySig.Encode(bcpgOut); + } + + for (int i = 0; i != ids.Count; i++) + { + if (ids[i] is string) + { + string id = (string) ids[i]; + + bcpgOut.WritePacket(new UserIdPacket(id)); + } + else + { + PgpUserAttributeSubpacketVector v = (PgpUserAttributeSubpacketVector)ids[i]; + bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray())); + } + + if (idTrusts[i] != null) + { + bcpgOut.WritePacket((ContainedPacket)idTrusts[i]); + } + + foreach (PgpSignature sig in (ArrayList) idSigs[i]) + { + sig.Encode(bcpgOut); + } + } + } + else + { + foreach (PgpSignature subSig in subSigs) + { + subSig.Encode(bcpgOut); + } + } + } + + /// Check whether this (sub)key has a revocation signature on it. + /// True, if this (sub)key has been revoked. + public bool IsRevoked() + { + int ns = 0; + bool revoked = false; + if (IsMasterKey) // Master key + { + while (!revoked && (ns < keySigs.Count)) + { + if (((PgpSignature)keySigs[ns++]).SignatureType == PgpSignature.KeyRevocation) + { + revoked = true; + } + } + } + else // Sub-key + { + while (!revoked && (ns < subSigs.Count)) + { + if (((PgpSignature)subSigs[ns++]).SignatureType == PgpSignature.SubkeyRevocation) + { + revoked = true; + } + } + } + return revoked; + } + + /// Add a certification for an id to the given public key. + /// The key the certification is to be added to. + /// The ID the certification is associated with. + /// The new certification. + /// The re-certified key. + public static PgpPublicKey AddCertification( + PgpPublicKey key, + string id, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + ArrayList sigList = null; + + for (int i = 0; i != returnKey.ids.Count; i++) + { + if (id.Equals(returnKey.ids[i])) + { + sigList = (ArrayList) returnKey.idSigs[i]; + } + } + + if (sigList != null) + { + sigList.Add(certification); + } + else + { + sigList = new ArrayList(); + + sigList.Add(certification); + returnKey.ids.Add(id); + returnKey.idTrusts.Add(null); + returnKey.idSigs.Add(sigList); + } + + return returnKey; + } + + /// Add a certification for the given UserAttributeSubpackets to the given public key. + /// The key the certification is to be added to. + /// The attributes the certification is associated with. + /// The new certification. + /// The re-certified key. + public static PgpPublicKey AddCertification( + PgpPublicKey key, + PgpUserAttributeSubpacketVector userAttributes, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + IList sigList = null; + + for (int i = 0; i != returnKey.ids.Count; i++) + { + if (userAttributes.Equals(returnKey.ids[i])) + { + sigList = (IList) returnKey.idSigs[i]; + } + } + + if (sigList != null) + { + sigList.Add(certification); + } + else + { + sigList = new ArrayList(); + sigList.Add(certification); + returnKey.ids.Add(userAttributes); + returnKey.idTrusts.Add(null); + returnKey.idSigs.Add(sigList); + } + + return returnKey; + } + + /// + /// Remove any certifications associated with a user attribute subpacket on a key. + /// + /// The key the certifications are to be removed from. + /// The attributes to be removed. + /// + /// The re-certified key, or null if the user attribute subpacket was not found on the key. + /// + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + PgpUserAttributeSubpacketVector userAttributes) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + bool found = false; + + for (int i = 0; i < returnKey.ids.Count; i++) + { + if (userAttributes.Equals(returnKey.ids[i])) + { + found = true; + returnKey.ids.RemoveAt(i); + returnKey.idTrusts.RemoveAt(i); + returnKey.idSigs.RemoveAt(i); + } + } + + if (!found) + { + return null; + } + + return returnKey; + } + + /// Remove any certifications associated with a given ID on a key. + /// The key the certifications are to be removed from. + /// The ID that is to be removed. + /// The re-certified key, or null if the ID was not found on the key. + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + string id) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + bool found = false; + + for (int i = 0; i < returnKey.ids.Count; i++) + { + if (id.Equals(returnKey.ids[i])) + { + found = true; + returnKey.ids.RemoveAt(i); + returnKey.idTrusts.RemoveAt(i); + returnKey.idSigs.RemoveAt(i); + } + } + + return found ? returnKey : null; + } + + /// Remove any certifications associated with a given ID on a key. + /// The key the certifications are to be removed from. + /// The ID that the certfication is to be removed from. + /// The certfication to be removed. + /// The re-certified key, or null if the certification was not found. + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + string id, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + bool found = false; + + for (int i = 0; i < returnKey.ids.Count; i++) + { + if (id.Equals(returnKey.ids[i])) + { + ArrayList certs = (ArrayList) returnKey.idSigs[i]; + found = certs.Contains(certification); + + if (found) + { + certs.Remove(certification); + } + } + } + + return found ? returnKey : null; + } + + /// Add a revocation or some other key certification to a key. + /// The key the revocation is to be added to. + /// The key signature to be added. + /// The new changed public key object. + public static PgpPublicKey AddCertification( + PgpPublicKey key, + PgpSignature certification) + { + if (key.IsMasterKey) + { + if (certification.SignatureType == PgpSignature.SubkeyRevocation) + { + throw new ArgumentException("signature type incorrect for master key revocation."); + } + } + else + { + if (certification.SignatureType == PgpSignature.KeyRevocation) + { + throw new ArgumentException("signature type incorrect for sub-key revocation."); + } + } + + PgpPublicKey returnKey = new PgpPublicKey(key); + + if (returnKey.subSigs != null) + { + returnKey.subSigs.Add(certification); + } + else + { + returnKey.keySigs.Add(certification); + } + + return returnKey; + } + } +} diff --git a/src/core/srcbc/openpgp/PgpPublicKeyEncryptedData.cs b/src/core/srcbc/openpgp/PgpPublicKeyEncryptedData.cs new file mode 100644 index 0000000..d6b0fec --- /dev/null +++ b/src/core/srcbc/openpgp/PgpPublicKeyEncryptedData.cs @@ -0,0 +1,233 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// A public key encrypted data object. + public class PgpPublicKeyEncryptedData + : PgpEncryptedData + { + private PublicKeyEncSessionPacket keyData; + + internal PgpPublicKeyEncryptedData( + PublicKeyEncSessionPacket keyData, + InputStreamPacket encData) + : base(encData) + { + this.keyData = keyData; + } + + private static IBufferedCipher GetKeyCipher( + PublicKeyAlgorithmTag algorithm) + { + try + { + switch (algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + return CipherUtilities.GetCipher("RSA//PKCS1Padding"); + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + return CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding"); + default: + throw new PgpException("unknown asymmetric algorithm: " + algorithm); + } + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + } + + private bool ConfirmCheckSum( + byte[] sessionInfo) + { + int check = 0; + + for (int i = 1; i != sessionInfo.Length - 2; i++) + { + check += sessionInfo[i] & 0xff; + } + + return (sessionInfo[sessionInfo.Length - 2] == (byte)(check >> 8)) + && (sessionInfo[sessionInfo.Length - 1] == (byte)(check)); + } + + /// The key ID for the key used to encrypt the data. + public long KeyId + { + get { return keyData.KeyId; } + } + + /// Return the decrypted data stream for the packet. + public Stream GetDataStream( + PgpPrivateKey privKey) + { + IBufferedCipher c1 = GetKeyCipher(keyData.Algorithm); + + try + { + c1.Init(false, privKey.Key); + } + catch (InvalidKeyException e) + { + throw new PgpException("error setting asymmetric cipher", e); + } + + BigInteger[] keyD = keyData.GetEncSessionKey(); + + if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt + || keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral) + { + c1.ProcessBytes(keyD[0].ToByteArrayUnsigned()); + } + else + { + ElGamalPrivateKeyParameters k = (ElGamalPrivateKeyParameters)privKey.Key; + int size = (k.Parameters.P.BitLength + 7) / 8; + + byte[] bi = keyD[0].ToByteArray(); + + int diff = bi.Length - size; + if (diff >= 0) + { + c1.ProcessBytes(bi, diff, size); + } + else + { + byte[] zeros = new byte[-diff]; + c1.ProcessBytes(zeros); + c1.ProcessBytes(bi); + } + + bi = keyD[1].ToByteArray(); + + diff = bi.Length - size; + if (diff >= 0) + { + c1.ProcessBytes(bi, diff, size); + } + else + { + byte[] zeros = new byte[-diff]; + c1.ProcessBytes(zeros); + c1.ProcessBytes(bi); + } + } + + byte[] plain; + try + { + plain = c1.DoFinal(); + } + catch (Exception e) + { + throw new PgpException("exception decrypting secret key", e); + } + + if (!ConfirmCheckSum(plain)) + throw new PgpKeyValidationException("key checksum failed"); + + IBufferedCipher c2; + string cipherName = PgpUtilities.GetSymmetricCipherName((SymmetricKeyAlgorithmTag) plain[0]); + string cName = cipherName; + + try + { + if (encData is SymmetricEncIntegrityPacket) + { + cName += "/CFB/NoPadding"; + } + else + { + cName += "/OpenPGPCFB/NoPadding"; + } + + c2 = CipherUtilities.GetCipher(cName); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("exception creating cipher", e); + } + + if (c2 == null) + return encData.GetInputStream(); + + try + { + KeyParameter key = ParameterUtilities.CreateKeyParameter( + cipherName, plain, 1, plain.Length - 3); + + byte[] iv = new byte[c2.GetBlockSize()]; + + c2.Init(false, new ParametersWithIV(key, iv)); + + encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c2, null)); + + if (encData is SymmetricEncIntegrityPacket) + { + truncStream = new TruncatedStream(encStream); + + string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1); + IDigest digest = DigestUtilities.GetDigest(digestName); + + encStream = new DigestStream(truncStream, digest, null); + } + + if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length) + throw new EndOfStreamException("unexpected end of stream."); + + int v1 = encStream.ReadByte(); + int v2 = encStream.ReadByte(); + + if (v1 < 0 || v2 < 0) + throw new EndOfStreamException("unexpected end of stream."); + + // Note: the oracle attack on the "quick check" bytes is deemed + // a security risk for typical public key encryption usages, + // therefore we do not perform the check. + +// bool repeatCheckPassed = +// iv[iv.Length - 2] == (byte)v1 +// && iv[iv.Length - 1] == (byte)v2; +// +// // Note: some versions of PGP appear to produce 0 for the extra +// // bytes rather than repeating the two previous bytes +// bool zeroesCheckPassed = +// v1 == 0 +// && v2 == 0; +// +// if (!repeatCheckPassed && !zeroesCheckPassed) +// { +// throw new PgpDataValidationException("quick check failed."); +// } + + return encStream; + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception starting decryption", e); + } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpPublicKeyRing.cs b/src/core/srcbc/openpgp/PgpPublicKeyRing.cs new file mode 100644 index 0000000..631e427 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpPublicKeyRing.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// Class to hold a single master public key and its subkeys. + ///

    + /// Often PGP keyring files consist of multiple master keys, if you are trying to process + /// or construct one of these you should use the PgpPublicKeyRingBundle class. + ///

    + ///
    + public class PgpPublicKeyRing + : PgpKeyRing + { + private readonly ArrayList keys; + + public PgpPublicKeyRing( + byte[] encoding) + : this(new MemoryStream(encoding, false)) + { + } + + internal PgpPublicKeyRing( + ArrayList pubKeys) + { + this.keys = pubKeys; + } + + public PgpPublicKeyRing( + Stream inputStream) + { + this.keys = new ArrayList(); + + BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); + + PacketTag initialTag = bcpgInput.NextPacketTag(); + if (initialTag != PacketTag.PublicKey && initialTag != PacketTag.PublicSubkey) + { + throw new IOException("public key ring doesn't start with public key tag: " + + "tag 0x" + ((int)initialTag).ToString("X")); + } + + PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();; + TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput); + + // direct signatures and revocations + ArrayList keySigs = ReadSignaturesAndTrust(bcpgInput); + + ArrayList ids, idTrusts, idSigs; + ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); + + keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs)); + + + // Read subkeys + while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) + { + PublicKeyPacket pk = (PublicKeyPacket) bcpgInput.ReadPacket(); + TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput); + + // PGP 8 actually leaves out the signature. + ArrayList sigList = ReadSignaturesAndTrust(bcpgInput); + + keys.Add(new PgpPublicKey(pk, kTrust, sigList)); + } + } + + /// Return the first public key in the ring. + public PgpPublicKey GetPublicKey() + { + return (PgpPublicKey) keys[0]; + } + + /// Return the public key referred to by the passed in key ID if it is present. + public PgpPublicKey GetPublicKey( + long keyId) + { + foreach (PgpPublicKey k in keys) + { + if (keyId == k.KeyId) + { + return k; + } + } + + return null; + } + + /// Allows enumeration of all the public keys. + /// An IEnumerable of PgpPublicKey objects. + public IEnumerable GetPublicKeys() + { + return new EnumerableProxy(keys); + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + foreach (PgpPublicKey k in keys) + { + k.Encode(outStr); + } + } + + /// + /// Returns a new key ring with the public key passed in either added or + /// replacing an existing one. + /// + /// The public key ring to be modified. + /// The public key to be inserted. + /// A new PgpPublicKeyRing + public static PgpPublicKeyRing InsertPublicKey( + PgpPublicKeyRing pubRing, + PgpPublicKey pubKey) + { + ArrayList keys = new ArrayList(pubRing.keys); + bool found = false; + bool masterFound = false; + + for (int i = 0; i != keys.Count; i++) + { + PgpPublicKey key = (PgpPublicKey) keys[i]; + + if (key.KeyId == pubKey.KeyId) + { + found = true; + keys[i] = pubKey; + } + if (key.IsMasterKey) + { + masterFound = true; + } + } + + if (!found) + { + if (pubKey.IsMasterKey && masterFound) + throw new ArgumentException("cannot add a master key to a ring that already has one"); + + keys.Add(pubKey); + } + + return new PgpPublicKeyRing(keys); + } + + /// Returns a new key ring with the public key passed in removed from the key ring. + /// The public key ring to be modified. + /// The public key to be removed. + /// A new PgpPublicKeyRing, or null if pubKey is not found. + public static PgpPublicKeyRing RemovePublicKey( + PgpPublicKeyRing pubRing, + PgpPublicKey pubKey) + { + ArrayList keys = new ArrayList(pubRing.keys); + bool found = false; + + for (int i = 0; i < keys.Count; i++) + { + PgpPublicKey key = (PgpPublicKey) keys[i]; + + if (key.KeyId == pubKey.KeyId) + { + found = true; + keys.RemoveAt(i); + } + } + + return found ? new PgpPublicKeyRing(keys) : null; + } + } +} diff --git a/src/core/srcbc/openpgp/PgpPublicKeyRingBundle.cs b/src/core/srcbc/openpgp/PgpPublicKeyRingBundle.cs new file mode 100644 index 0000000..ccb19d6 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpPublicKeyRingBundle.cs @@ -0,0 +1,269 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// Often a PGP key ring file is made up of a succession of master/sub-key key rings. + /// If you want to read an entire public key file in one hit this is the class for you. + /// + public class PgpPublicKeyRingBundle + { + private readonly IDictionary pubRings; + private readonly ArrayList order; + + private PgpPublicKeyRingBundle( + IDictionary pubRings, + ArrayList order) + { + this.pubRings = pubRings; + this.order = order; + } + + public PgpPublicKeyRingBundle( + byte[] encoding) + : this(new MemoryStream(encoding, false)) + { + } + + /// Build a PgpPublicKeyRingBundle from the passed in input stream. + /// Input stream containing data. + /// If a problem parsing the stream occurs. + /// If an object is encountered which isn't a PgpPublicKeyRing. + public PgpPublicKeyRingBundle( + Stream inputStream) + : this(new PgpObjectFactory(inputStream).AllPgpObjects()) + { + } + + public PgpPublicKeyRingBundle( + IEnumerable e) + { + this.pubRings = new Hashtable(); + this.order = new ArrayList(); + + foreach (object obj in e) + { + PgpPublicKeyRing pgpPub = obj as PgpPublicKeyRing; + + if (pgpPub == null) + { + throw new PgpException(obj.GetType().FullName + " found where PgpPublicKeyRing expected"); + } + + long key = pgpPub.GetPublicKey().KeyId; + pubRings.Add(key, pgpPub); + order.Add(key); + } + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return order.Count; } + } + + /// Return the number of key rings in this collection. + public int Count + { + get { return order.Count; } + } + + /// Allow enumeration of the public key rings making up this collection. + public IEnumerable GetKeyRings() + { + return new EnumerableProxy(pubRings.Values); + } + + /// Allow enumeration of the key rings associated with the passed in userId. + /// The user ID to be matched. + /// An IEnumerable of key rings which matched (possibly none). + public IEnumerable GetKeyRings( + string userId) + { + return GetKeyRings(userId, false, false); + } + + /// Allow enumeration of the key rings associated with the passed in userId. + /// The user ID to be matched. + /// If true, userId need only be a substring of an actual ID string to match. + /// An IEnumerable of key rings which matched (possibly none). + public IEnumerable GetKeyRings( + string userId, + bool matchPartial) + { + return GetKeyRings(userId, matchPartial, false); + } + + /// Allow enumeration of the key rings associated with the passed in userId. + /// The user ID to be matched. + /// If true, userId need only be a substring of an actual ID string to match. + /// If true, case is ignored in user ID comparisons. + /// An IEnumerable of key rings which matched (possibly none). + public IEnumerable GetKeyRings( + string userId, + bool matchPartial, + bool ignoreCase) + { + IList rings = new ArrayList(); + + if (ignoreCase) + { + userId = userId.ToLower(CultureInfo.InvariantCulture); + } + + foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + { + foreach (string nextUserID in pubRing.GetPublicKey().GetUserIds()) + { + string next = nextUserID; + if (ignoreCase) + { + next = next.ToLower(CultureInfo.InvariantCulture); + } + + if (matchPartial) + { + if (next.IndexOf(userId) > -1) + { + rings.Add(pubRing); + } + } + else + { + if (next.Equals(userId)) + { + rings.Add(pubRing); + } + } + } + } + + return new EnumerableProxy(rings); + } + + /// Return the PGP public key associated with the given key id. + /// The ID of the public key to return. + public PgpPublicKey GetPublicKey( + long keyId) + { + foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + { + PgpPublicKey pub = pubRing.GetPublicKey(keyId); + + if (pub != null) + { + return pub; + } + } + + return null; + } + + /// Return the public key ring which contains the key referred to by keyId + /// The ID of the public key + public PgpPublicKeyRing GetPublicKeyRing( + long keyId) + { + if (pubRings.Contains(keyId)) + { + return (PgpPublicKeyRing)pubRings[keyId]; + } + + foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + { + PgpPublicKey pub = pubRing.GetPublicKey(keyId); + + if (pub != null) + { + return pubRing; + } + } + + return null; + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); + + foreach (long key in order) + { + PgpPublicKeyRing sec = (PgpPublicKeyRing) pubRings[key]; + + sec.Encode(bcpgOut); + } + } + + /// + /// Return a new bundle containing the contents of the passed in bundle and + /// the passed in public key ring. + /// + /// The PgpPublicKeyRingBundle the key ring is to be added to. + /// The key ring to be added. + /// A new PgpPublicKeyRingBundle merging the current one with the passed in key ring. + /// If the keyId for the passed in key ring is already present. + public static PgpPublicKeyRingBundle AddPublicKeyRing( + PgpPublicKeyRingBundle bundle, + PgpPublicKeyRing publicKeyRing) + { + long key = publicKeyRing.GetPublicKey().KeyId; + + if (bundle.pubRings.Contains(key)) + { + throw new ArgumentException("Bundle already contains a key with a keyId for the passed in ring."); + } + + IDictionary newPubRings = new Hashtable(bundle.pubRings); + ArrayList newOrder = new ArrayList(bundle.order); + + newPubRings[key] = publicKeyRing; + + newOrder.Add(key); + + return new PgpPublicKeyRingBundle(newPubRings, newOrder); + } + + /// + /// Return a new bundle containing the contents of the passed in bundle with + /// the passed in public key ring removed. + /// + /// The PgpPublicKeyRingBundle the key ring is to be removed from. + /// The key ring to be removed. + /// A new PgpPublicKeyRingBundle not containing the passed in key ring. + /// If the keyId for the passed in key ring is not present. + public static PgpPublicKeyRingBundle RemovePublicKeyRing( + PgpPublicKeyRingBundle bundle, + PgpPublicKeyRing publicKeyRing) + { + long key = publicKeyRing.GetPublicKey().KeyId; + + if (!bundle.pubRings.Contains(key)) + { + throw new ArgumentException("Bundle does not contain a key with a keyId for the passed in ring."); + } + + IDictionary newPubRings = new Hashtable(bundle.pubRings); + ArrayList newOrder = new ArrayList(bundle.order); + + newPubRings.Remove(key); + newOrder.Remove(key); + + return new PgpPublicKeyRingBundle(newPubRings, newOrder); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpSecretKey.cs b/src/core/srcbc/openpgp/PgpSecretKey.cs new file mode 100644 index 0000000..2069da6 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpSecretKey.cs @@ -0,0 +1,707 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// General class to handle a PGP secret key object. + public class PgpSecretKey + { + private SecretKeyPacket secret; + private TrustPacket trust; + private ArrayList keySigs; + private ArrayList ids; + private ArrayList idTrusts; + private ArrayList idSigs; + private PgpPublicKey pub; + private ArrayList subSigs; + + /// Copy constructor - master key. + private PgpSecretKey( + SecretKeyPacket secret, + TrustPacket trust, + ArrayList keySigs, + ArrayList ids, + ArrayList idTrusts, + ArrayList idSigs, + PgpPublicKey pub) + { + this.secret = secret; + this.trust = trust; + this.keySigs = keySigs; + this.ids = ids; + this.idTrusts = idTrusts; + this.idSigs = idSigs; + this.pub = pub; + } + + /// Copy constructor - subkey. + private PgpSecretKey( + SecretKeyPacket secret, + TrustPacket trust, + ArrayList subSigs, + PgpPublicKey pub) + { + this.secret = secret; + this.trust = trust; + this.subSigs = subSigs; + this.pub = pub; + } + + internal PgpSecretKey( + SecretKeyPacket secret, + TrustPacket trust, + ArrayList keySigs, + ArrayList ids, + ArrayList idTrusts, + ArrayList idSigs) + { + this.secret = secret; + this.trust = trust; + this.keySigs = keySigs; + this.ids = ids; + this.idTrusts = idTrusts; + this.idSigs = idSigs; + this.pub = new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs); + } + + internal PgpSecretKey( + SecretKeyPacket secret, + TrustPacket trust, + ArrayList subSigs) + { + this.secret = secret; + this.trust = trust; + this.subSigs = subSigs; + this.pub = new PgpPublicKey(secret.PublicKeyPacket, trust, subSigs); + } + + /// Create a subkey + internal PgpSecretKey( + PgpKeyPair keyPair, + TrustPacket trust, + ArrayList subSigs, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSHA1, + SecureRandom rand) + : this(keyPair, encAlgorithm, passPhrase, useSHA1, rand) + { + this.secret = new SecretSubkeyPacket( + secret.PublicKeyPacket, + secret.EncAlgorithm, + secret.S2kUsage, + secret.S2k, + secret.GetIV(), + secret.GetSecretKeyData()); + + this.trust = trust; + this.subSigs = subSigs; + this.pub = new PgpPublicKey(keyPair.PublicKey, trust, subSigs); + } + + internal PgpSecretKey( + PgpKeyPair keyPair, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSHA1, + SecureRandom rand) + { + PublicKeyPacket pubPk = keyPair.PublicKey.publicPk; + + BcpgObject secKey; + switch (keyPair.PublicKey.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaSign: + case PublicKeyAlgorithmTag.RsaGeneral: + RsaPrivateCrtKeyParameters rsK = (RsaPrivateCrtKeyParameters) keyPair.PrivateKey.Key; + secKey = new RsaSecretBcpgKey(rsK.Exponent, rsK.P, rsK.Q); + break; + case PublicKeyAlgorithmTag.Dsa: + DsaPrivateKeyParameters dsK = (DsaPrivateKeyParameters) keyPair.PrivateKey.Key; + secKey = new DsaSecretBcpgKey(dsK.X); + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) keyPair.PrivateKey.Key; + secKey = new ElGamalSecretBcpgKey(esK.X); + break; + default: + throw new PgpException("unknown key class"); + } + + try + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.WriteObject(secKey); + + byte[] keyData = bOut.ToArray(); + byte[] checksumBytes = Checksum(useSHA1, keyData, keyData.Length); + + pOut.Write(checksumBytes); + + byte[] bOutData = bOut.ToArray(); + + if (encAlgorithm == SymmetricKeyAlgorithmTag.Null) + { + this.secret = new SecretKeyPacket(pubPk, encAlgorithm, null, null, bOutData); + } + else + { + S2k s2k; + byte[] iv; + byte[] encData = EncryptKeyData(bOutData, encAlgorithm, passPhrase, rand, out s2k, out iv); + + int usage = useSHA1 + ? SecretKeyPacket.UsageSha1 + : SecretKeyPacket.UsageChecksum; + + this.secret = new SecretKeyPacket(pubPk, encAlgorithm, usage, s2k, iv, encData); + } + + this.trust = null; + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception encrypting key", e); + } + + this.keySigs = new ArrayList(); + } + + public PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand) + { + } + + public PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSHA1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(keyPair, encAlgorithm, passPhrase, useSHA1, rand) + { + try + { + this.trust = null; + this.ids = new ArrayList(); + ids.Add(id); + + this.idTrusts = new ArrayList(); + idTrusts.Add(null); + + this.idSigs = new ArrayList(); + + PgpSignatureGenerator sGen = new PgpSignatureGenerator( + keyPair.PublicKey.Algorithm, HashAlgorithmTag.Sha1); + + // + // Generate the certification + // + sGen.InitSign(certificationLevel, keyPair.PrivateKey); + + sGen.SetHashedSubpackets(hashedPackets); + sGen.SetUnhashedSubpackets(unhashedPackets); + + PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey); + this.pub = PgpPublicKey.AddCertification(keyPair.PublicKey, id, certification); + + ArrayList sigList = new ArrayList(); + sigList.Add(certification); + idSigs.Add(sigList); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception encrypting key", e); + } + } + + public PgpSecretKey( + int certificationLevel, + PublicKeyAlgorithmTag algorithm, + AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter privKey, + DateTime time, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, + new PgpKeyPair(algorithm, pubKey, privKey, time), + id, encAlgorithm, passPhrase, hashedPackets, unhashedPackets, rand) + { + } + + public PgpSecretKey( + int certificationLevel, + PublicKeyAlgorithmTag algorithm, + AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter privKey, + DateTime time, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSHA1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, new PgpKeyPair(algorithm, pubKey, privKey, time), id, encAlgorithm, passPhrase, useSHA1, hashedPackets, unhashedPackets, rand) + { + } + + /// True, if this key is marked as suitable for signature generation. + public bool IsSigningKey + { + get + { + switch (pub.Algorithm) + { + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + case PublicKeyAlgorithmTag.Dsa: + case PublicKeyAlgorithmTag.ECDsa: + case PublicKeyAlgorithmTag.ElGamalGeneral: + return true; + default: + return false; + } + } + } + + /// True, if this is a master key. + public bool IsMasterKey + { + get { return subSigs == null; } + } + + /// The algorithm the key is encrypted with. + public SymmetricKeyAlgorithmTag KeyEncryptionAlgorithm + { + get { return secret.EncAlgorithm; } + } + + /// The key ID of the public key associated with this key. + public long KeyId + { + get { return pub.KeyId; } + } + + /// The public key associated with this key. + public PgpPublicKey PublicKey + { + get { return pub; } + } + + /// Allows enumeration of any user IDs associated with the key. + /// An IEnumerable of string objects. + public IEnumerable UserIds + { + get { return pub.GetUserIds(); } + } + + /// Allows enumeration of any user attribute vectors associated with the key. + /// An IEnumerable of string objects. + public IEnumerable UserAttributes + { + get { return pub.GetUserAttributes(); } + } + + private byte[] ExtractKeyData( + char[] passPhrase) + { + SymmetricKeyAlgorithmTag alg = secret.EncAlgorithm; + byte[] encData = secret.GetSecretKeyData(); + + if (alg == SymmetricKeyAlgorithmTag.Null) + return encData; + + byte[] data; + IBufferedCipher c = null; + try + { + string cName = PgpUtilities.GetSymmetricCipherName(alg); + c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding"); + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + + // TODO Factor this block out as 'encryptData' + try + { + KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase); + byte[] iv = secret.GetIV(); + + if (secret.PublicKeyPacket.Version == 4) + { + c.Init(false, new ParametersWithIV(key, iv)); + + data = c.DoFinal(encData); + + bool useSHA1 = secret.S2kUsage == SecretKeyPacket.UsageSha1; + byte[] check = Checksum(useSHA1, data, (useSHA1) ? data.Length - 20 : data.Length - 2); + + for (int i = 0; i != check.Length; i++) + { + if (check[i] != data[data.Length - check.Length + i]) + { + throw new PgpException("Checksum mismatch at " + i + " of " + check.Length); + } + } + } + else // version 2 or 3, RSA only. + { + data = new byte[encData.Length]; + + // + // read in the four numbers + // + int pos = 0; + + for (int i = 0; i != 4; i++) + { + c.Init(false, new ParametersWithIV(key, iv)); + + int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + pos += 2; + + c.DoFinal(encData, pos, encLen, data, pos); + pos += encLen; + + if (i != 3) + { + Array.Copy(encData, pos - iv.Length, iv, 0, iv.Length); + } + } + + // + // verify Checksum + // + int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); + int calcCs = 0; + for (int j=0; j < data.Length-2; j++) + { + calcCs += data[j] & 0xff; + } + + calcCs &= 0xffff; + if (calcCs != cs) + { + throw new PgpException("Checksum mismatch: passphrase wrong, expected " + + cs.ToString("X") + + " found " + calcCs.ToString("X")); + } + } + + return data; + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception decrypting key", e); + } + } + + /// Extract a PgpPrivateKey from this secret key's encrypted contents. + public PgpPrivateKey ExtractPrivateKey( + char[] passPhrase) + { + byte[] secKeyData = secret.GetSecretKeyData(); + if (secKeyData == null || secKeyData.Length < 1) + return null; + + PublicKeyPacket pubPk = secret.PublicKeyPacket; + try + { + byte[] data = ExtractKeyData(passPhrase); + BcpgInputStream bcpgIn = BcpgInputStream.Wrap(new MemoryStream(data, false)); + AsymmetricKeyParameter privateKey; + switch (pubPk.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + RsaPublicBcpgKey rsaPub = (RsaPublicBcpgKey)pubPk.Key; + RsaSecretBcpgKey rsaPriv = new RsaSecretBcpgKey(bcpgIn); + RsaPrivateCrtKeyParameters rsaPrivSpec = new RsaPrivateCrtKeyParameters( + rsaPriv.Modulus, + rsaPub.PublicExponent, + rsaPriv.PrivateExponent, + rsaPriv.PrimeP, + rsaPriv.PrimeQ, + rsaPriv.PrimeExponentP, + rsaPriv.PrimeExponentQ, + rsaPriv.CrtCoefficient); + privateKey = rsaPrivSpec; + break; + case PublicKeyAlgorithmTag.Dsa: + DsaPublicBcpgKey dsaPub = (DsaPublicBcpgKey)pubPk.Key; + DsaSecretBcpgKey dsaPriv = new DsaSecretBcpgKey(bcpgIn); + DsaParameters dsaParams = new DsaParameters(dsaPub.P, dsaPub.Q, dsaPub.G); + privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams); + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key; + ElGamalSecretBcpgKey elPriv = new ElGamalSecretBcpgKey(bcpgIn); + ElGamalParameters elParams = new ElGamalParameters(elPub.P, elPub.G); + privateKey = new ElGamalPrivateKeyParameters(elPriv.X, elParams); + break; + default: + throw new PgpException("unknown public key algorithm encountered"); + } + + return new PgpPrivateKey(privateKey, KeyId); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception constructing key", e); + } + } + + private static byte[] Checksum( + bool useSHA1, + byte[] bytes, + int length) + { + if (useSHA1) + { + try + { + IDigest dig = DigestUtilities.GetDigest("SHA1"); + dig.BlockUpdate(bytes, 0, length); + return DigestUtilities.DoFinal(dig); + } + //catch (NoSuchAlgorithmException e) + catch (Exception e) + { + throw new PgpException("Can't find SHA-1", e); + } + } + else + { + int Checksum = 0; + for (int i = 0; i != length; i++) + { + Checksum += bytes[i]; + } + + return new byte[] { (byte)(Checksum >> 8), (byte)Checksum }; + } + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + Encode(bOut); + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); + + bcpgOut.WritePacket(secret); + if (trust != null) + { + bcpgOut.WritePacket(trust); + } + + if (subSigs == null) // is not a sub key + { + foreach (PgpSignature keySig in keySigs) + { + keySig.Encode(bcpgOut); + } + + for (int i = 0; i != ids.Count; i++) + { + if (ids[i] is string) + { + string id = (string) ids[i]; + + bcpgOut.WritePacket(new UserIdPacket(id)); + } + else + { + PgpUserAttributeSubpacketVector v = (PgpUserAttributeSubpacketVector)ids[i]; + bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray())); + } + + if (idTrusts[i] != null) + { + bcpgOut.WritePacket((ContainedPacket)idTrusts[i]); + } + + foreach (PgpSignature sig in (ArrayList) idSigs[i]) + { + sig.Encode(bcpgOut); + } + } + } + else + { + foreach (PgpSignature subSig in subSigs) + { + subSig.Encode(bcpgOut); + } + } + + // TODO Check that this is right/necessary + //bcpgOut.Finish(); + } + + /// + /// Return a copy of the passed in secret key, encrypted using a new password + /// and the passed in algorithm. + /// + /// The PgpSecretKey to be copied. + /// The current password for the key. + /// The new password for the key. + /// The algorithm to be used for the encryption. + /// Source of randomness. + public static PgpSecretKey CopyWithNewPassword( + PgpSecretKey key, + char[] oldPassPhrase, + char[] newPassPhrase, + SymmetricKeyAlgorithmTag newEncAlgorithm, + SecureRandom rand) + { + byte[] rawKeyData = key.ExtractKeyData(oldPassPhrase); + int s2kUsage = key.secret.S2kUsage; + byte[] iv = null; + S2k s2k = null; + byte[] keyData; + + if (newEncAlgorithm == SymmetricKeyAlgorithmTag.Null) + { + s2kUsage = SecretKeyPacket.UsageNone; + if (key.secret.S2kUsage == SecretKeyPacket.UsageSha1) // SHA-1 hash, need to rewrite Checksum + { + keyData = new byte[rawKeyData.Length - 18]; + + Array.Copy(rawKeyData, 0, keyData, 0, keyData.Length - 2); + + byte[] check = Checksum(false, keyData, keyData.Length - 2); + + keyData[keyData.Length - 2] = check[0]; + keyData[keyData.Length - 1] = check[1]; + } + else + { + keyData = rawKeyData; + } + } + else + { + try + { + keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception encrypting key", e); + } + } + + SecretKeyPacket secret; + if (key.secret is SecretSubkeyPacket) + { + secret = new SecretSubkeyPacket(key.secret.PublicKeyPacket, + newEncAlgorithm, s2kUsage, s2k, iv, keyData); + } + else + { + secret = new SecretKeyPacket(key.secret.PublicKeyPacket, + newEncAlgorithm, s2kUsage, s2k, iv, keyData); + } + + if (key.subSigs == null) + { + return new PgpSecretKey(secret, key.trust, key.keySigs, key.ids, + key.idTrusts, key.idSigs, key.pub); + } + + return new PgpSecretKey(secret, key.trust, key.subSigs, key.pub); + } + + private static byte[] EncryptKeyData( + byte[] rawKeyData, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + SecureRandom random, + out S2k s2k, + out byte[] iv) + { + IBufferedCipher c; + try + { + string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm); + c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding"); + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + + byte[] s2kIV = new byte[8]; + random.NextBytes(s2kIV); + s2k = new S2k(HashAlgorithmTag.Sha1, s2kIV, 0x60); + + KeyParameter kp = PgpUtilities.MakeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase); + + iv = new byte[c.GetBlockSize()]; + random.NextBytes(iv); + + c.Init(true, new ParametersWithRandom(new ParametersWithIV(kp, iv), random)); + + return c.DoFinal(rawKeyData); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpSecretKeyRing.cs b/src/core/srcbc/openpgp/PgpSecretKeyRing.cs new file mode 100644 index 0000000..b6fa630 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpSecretKeyRing.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// Class to hold a single master secret key and its subkeys. + ///

    + /// Often PGP keyring files consist of multiple master keys, if you are trying to process + /// or construct one of these you should use the PgpSecretKeyRingBundle class. + ///

    + ///
    + public class PgpSecretKeyRing + : PgpKeyRing + { + private readonly ArrayList keys; + + internal PgpSecretKeyRing( + ArrayList keys) + { + this.keys = keys; + } + + public PgpSecretKeyRing( + byte[] encoding) + : this(new MemoryStream(encoding)) + { + } + + public PgpSecretKeyRing( + Stream inputStream) + { + this.keys = new ArrayList(); + + BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); + + PacketTag initialTag = bcpgInput.NextPacketTag(); + if (initialTag != PacketTag.SecretKey && initialTag != PacketTag.SecretSubkey) + { + throw new IOException("secret key ring doesn't start with secret key tag: " + + "tag 0x" + ((int)initialTag).ToString("X")); + } + + SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket(); + + // + // ignore GPG comment packets if found. + // + while (bcpgInput.NextPacketTag() == PacketTag.Experimental2) + { + bcpgInput.ReadPacket(); + } + + TrustPacket trust = ReadOptionalTrustPacket(bcpgInput); + + // revocation and direct signatures + ArrayList keySigs = ReadSignaturesAndTrust(bcpgInput); + + ArrayList ids, idTrusts, idSigs; + ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); + + keys.Add(new PgpSecretKey(secret, trust, keySigs, ids, idTrusts, idSigs)); + + + // Read subkeys + while (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey) + { + SecretSubkeyPacket sub = (SecretSubkeyPacket) bcpgInput.ReadPacket(); + + // + // ignore GPG comment packets if found. + // + while (bcpgInput.NextPacketTag() == PacketTag.Experimental2) + { + bcpgInput.ReadPacket(); + } + + TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput); + ArrayList sigList = ReadSignaturesAndTrust(bcpgInput); + + keys.Add(new PgpSecretKey(sub, subTrust, sigList)); + } + } + + /// Return the public key for the master key. + public PgpPublicKey GetPublicKey() + { + return ((PgpSecretKey) keys[0]).PublicKey; + } + + /// Return the master private key. + public PgpSecretKey GetSecretKey() + { + return (PgpSecretKey) keys[0]; + } + + /// Allows enumeration of the secret keys. + /// An IEnumerable of PgpSecretKey objects. + public IEnumerable GetSecretKeys() + { + return new EnumerableProxy(keys); + } + + public PgpSecretKey GetSecretKey( + long keyId) + { + foreach (PgpSecretKey k in keys) + { + if (keyId == k.KeyId) + { + return k; + } + } + + return null; + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + foreach (PgpSecretKey k in keys) + { + k.Encode(outStr); + } + } + + /// + /// Returns a new key ring with the secret key passed in either added or + /// replacing an existing one with the same key ID. + /// + /// The secret key ring to be modified. + /// The secret key to be inserted. + /// A new PgpSecretKeyRing + public static PgpSecretKeyRing InsertSecretKey( + PgpSecretKeyRing secRing, + PgpSecretKey secKey) + { + ArrayList keys = new ArrayList(secRing.keys); + bool found = false; + bool masterFound = false; + + for (int i = 0; i != keys.Count; i++) + { + PgpSecretKey key = (PgpSecretKey) keys[i]; + + if (key.KeyId == secKey.KeyId) + { + found = true; + keys[i] = secKey; + } + if (key.IsMasterKey) + { + masterFound = true; + } + } + + if (!found) + { + if (secKey.IsMasterKey && masterFound) + throw new ArgumentException("cannot add a master key to a ring that already has one"); + + keys.Add(secKey); + } + + return new PgpSecretKeyRing(keys); + } + + /// Returns a new key ring with the secret key passed in removed from the key ring. + /// The secret key ring to be modified. + /// The secret key to be removed. + /// A new PgpSecretKeyRing, or null if secKey is not found. + public static PgpSecretKeyRing RemoveSecretKey( + PgpSecretKeyRing secRing, + PgpSecretKey secKey) + { + ArrayList keys = new ArrayList(secRing.keys); + bool found = false; + + for (int i = 0; i < keys.Count; i++) + { + PgpSecretKey key = (PgpSecretKey)keys[i]; + + if (key.KeyId == secKey.KeyId) + { + found = true; + keys.RemoveAt(i); + } + } + + return found ? new PgpSecretKeyRing(keys) : null; + } + } +} diff --git a/src/core/srcbc/openpgp/PgpSecretKeyRingBundle.cs b/src/core/srcbc/openpgp/PgpSecretKeyRingBundle.cs new file mode 100644 index 0000000..bcf2e94 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpSecretKeyRingBundle.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// + /// Often a PGP key ring file is made up of a succession of master/sub-key key rings. + /// If you want to read an entire secret key file in one hit this is the class for you. + /// + public class PgpSecretKeyRingBundle + { + private readonly IDictionary secretRings; + private readonly ArrayList order; + + private PgpSecretKeyRingBundle( + IDictionary secretRings, + ArrayList order) + { + this.secretRings = secretRings; + this.order = order; + } + + public PgpSecretKeyRingBundle( + byte[] encoding) + : this(new MemoryStream(encoding, false)) + { + } + + /// Build a PgpSecretKeyRingBundle from the passed in input stream. + /// Input stream containing data. + /// If a problem parsing the stream occurs. + /// If an object is encountered which isn't a PgpSecretKeyRing. + public PgpSecretKeyRingBundle( + Stream inputStream) + : this(new PgpObjectFactory(inputStream).AllPgpObjects()) + { + } + + public PgpSecretKeyRingBundle( + IEnumerable e) + { + this.secretRings = new Hashtable(); + this.order = new ArrayList(); + + foreach (object obj in e) + { + PgpSecretKeyRing pgpSecret = obj as PgpSecretKeyRing; + + if (pgpSecret == null) + { + throw new PgpException(obj.GetType().FullName + " found where PgpSecretKeyRing expected"); + } + + long key = pgpSecret.GetPublicKey().KeyId; + secretRings.Add(key, pgpSecret); + order.Add(key); + } + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return order.Count; } + } + + /// Return the number of rings in this collection. + public int Count + { + get { return order.Count; } + } + + /// Allow enumeration of the secret key rings making up this collection. + public IEnumerable GetKeyRings() + { + return new EnumerableProxy(secretRings.Values); + } + + /// Allow enumeration of the key rings associated with the passed in userId. + /// The user ID to be matched. + /// An IEnumerable of key rings which matched (possibly none). + public IEnumerable GetKeyRings( + string userId) + { + return GetKeyRings(userId, false, false); + } + + /// Allow enumeration of the key rings associated with the passed in userId. + /// The user ID to be matched. + /// If true, userId need only be a substring of an actual ID string to match. + /// An IEnumerable of key rings which matched (possibly none). + public IEnumerable GetKeyRings( + string userId, + bool matchPartial) + { + return GetKeyRings(userId, matchPartial, false); + } + + /// Allow enumeration of the key rings associated with the passed in userId. + /// The user ID to be matched. + /// If true, userId need only be a substring of an actual ID string to match. + /// If true, case is ignored in user ID comparisons. + /// An IEnumerable of key rings which matched (possibly none). + public IEnumerable GetKeyRings( + string userId, + bool matchPartial, + bool ignoreCase) + { + IList rings = new ArrayList(); + + if (ignoreCase) + { + userId = userId.ToLower(CultureInfo.InvariantCulture); + } + + foreach (PgpSecretKeyRing secRing in GetKeyRings()) + { + foreach (string nextUserID in secRing.GetSecretKey().UserIds) + { + string next = nextUserID; + if (ignoreCase) + { + next = next.ToLower(CultureInfo.InvariantCulture); + } + + if (matchPartial) + { + if (next.IndexOf(userId) > -1) + { + rings.Add(secRing); + } + } + else + { + if (next.Equals(userId)) + { + rings.Add(secRing); + } + } + } + } + + return new EnumerableProxy(rings); + } + + /// Return the PGP secret key associated with the given key id. + /// The ID of the secret key to return. + public PgpSecretKey GetSecretKey( + long keyId) + { + foreach (PgpSecretKeyRing secRing in GetKeyRings()) + { + PgpSecretKey sec = secRing.GetSecretKey(keyId); + + if (sec != null) + { + return sec; + } + } + + return null; + } + + /// Return the secret key ring which contains the key referred to by keyId + /// The ID of the secret key + public PgpSecretKeyRing GetSecretKeyRing( + long keyId) + { + long id = keyId; + + if (secretRings.Contains(id)) + { + return (PgpSecretKeyRing) secretRings[id]; + } + + foreach (PgpSecretKeyRing secretRing in GetKeyRings()) + { + PgpSecretKey secret = secretRing.GetSecretKey(keyId); + + if (secret != null) + { + return secretRing; + } + } + + return null; + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); + + foreach (long key in order) + { + PgpSecretKeyRing pub = (PgpSecretKeyRing) secretRings[key]; + + pub.Encode(bcpgOut); + } + } + + /// + /// Return a new bundle containing the contents of the passed in bundle and + /// the passed in secret key ring. + /// + /// The PgpSecretKeyRingBundle the key ring is to be added to. + /// The key ring to be added. + /// A new PgpSecretKeyRingBundle merging the current one with the passed in key ring. + /// If the keyId for the passed in key ring is already present. + public static PgpSecretKeyRingBundle AddSecretKeyRing( + PgpSecretKeyRingBundle bundle, + PgpSecretKeyRing secretKeyRing) + { + long key = secretKeyRing.GetPublicKey().KeyId; + + if (bundle.secretRings.Contains(key)) + { + throw new ArgumentException("Collection already contains a key with a keyId for the passed in ring."); + } + + IDictionary newSecretRings = new Hashtable(bundle.secretRings); + ArrayList newOrder = new ArrayList(bundle.order); + + newSecretRings[key] = secretKeyRing; + newOrder.Add(key); + + return new PgpSecretKeyRingBundle(newSecretRings, newOrder); + } + + /// + /// Return a new bundle containing the contents of the passed in bundle with + /// the passed in secret key ring removed. + /// + /// The PgpSecretKeyRingBundle the key ring is to be removed from. + /// The key ring to be removed. + /// A new PgpSecretKeyRingBundle not containing the passed in key ring. + /// If the keyId for the passed in key ring is not present. + public static PgpSecretKeyRingBundle RemoveSecretKeyRing( + PgpSecretKeyRingBundle bundle, + PgpSecretKeyRing secretKeyRing) + { + long key = secretKeyRing.GetPublicKey().KeyId; + + if (!bundle.secretRings.Contains(key)) + { + throw new ArgumentException("Collection does not contain a key with a keyId for the passed in ring."); + } + + IDictionary newSecretRings = new Hashtable(bundle.secretRings); + ArrayList newOrder = new ArrayList(bundle.order); + + newSecretRings.Remove(key); + newOrder.Remove(key); + + return new PgpSecretKeyRingBundle(newSecretRings, newOrder); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpSignature.cs b/src/core/srcbc/openpgp/PgpSignature.cs new file mode 100644 index 0000000..2499200 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpSignature.cs @@ -0,0 +1,409 @@ +using System; +using System.IO; +using Org.BouncyCastle.Asn1; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// A PGP signature object. + public class PgpSignature + { + public const int BinaryDocument = 0x00; + public const int CanonicalTextDocument = 0x01; + public const int StandAlone = 0x02; + + public const int DefaultCertification = 0x10; + public const int NoCertification = 0x11; + public const int CasualCertification = 0x12; + public const int PositiveCertification = 0x13; + + public const int SubkeyBinding = 0x18; + public const int DirectKey = 0x1f; + public const int KeyRevocation = 0x20; + public const int SubkeyRevocation = 0x28; + public const int CertificationRevocation = 0x30; + public const int Timestamp = 0x40; + + private readonly SignaturePacket sigPck; + private readonly int signatureType; + private readonly TrustPacket trustPck; + + private ISigner sig; + private byte lastb; // Initial value anything but '\r' + + internal PgpSignature( + BcpgInputStream bcpgInput) + : this((SignaturePacket)bcpgInput.ReadPacket()) + { + } + + internal PgpSignature( + SignaturePacket sigPacket) + : this(sigPacket, null) + { + } + + internal PgpSignature( + SignaturePacket sigPacket, + TrustPacket trustPacket) + { + if (sigPacket == null) + throw new ArgumentNullException("sigPacket"); + + this.sigPck = sigPacket; + this.signatureType = sigPck.SignatureType; + this.trustPck = trustPacket; + } + + private void GetSig() + { + this.sig = SignerUtilities.GetSigner( + PgpUtilities.GetSignatureName(sigPck.KeyAlgorithm, sigPck.HashAlgorithm)); + } + + /// The OpenPGP version number for this signature. + public int Version + { + get { return sigPck.Version; } + } + + /// The key algorithm associated with this signature. + public PublicKeyAlgorithmTag KeyAlgorithm + { + get { return sigPck.KeyAlgorithm; } + } + + /// The hash algorithm associated with this signature. + public HashAlgorithmTag HashAlgorithm + { + get { return sigPck.HashAlgorithm; } + } + + public void InitVerify( + PgpPublicKey pubKey) + { + lastb = 0; + if (sig == null) + { + GetSig(); + } + try + { + sig.Init(false, pubKey.GetKey()); + } + catch (InvalidKeyException e) + { + throw new PgpException("invalid key.", e); + } + } + + public void Update( + byte b) + { + if (signatureType == CanonicalTextDocument) + { + doCanonicalUpdateByte(b); + } + else + { + sig.Update(b); + } + } + + private void doCanonicalUpdateByte( + byte b) + { + if (b == '\r') + { + doUpdateCRLF(); + } + else if (b == '\n') + { + if (lastb != '\r') + { + doUpdateCRLF(); + } + } + else + { + sig.Update(b); + } + + lastb = b; + } + + private void doUpdateCRLF() + { + sig.Update((byte)'\r'); + sig.Update((byte)'\n'); + } + + public void Update( + params byte[] bytes) + { + Update(bytes, 0, bytes.Length); + } + + public void Update( + byte[] bytes, + int off, + int length) + { + if (signatureType == CanonicalTextDocument) + { + int finish = off + length; + + for (int i = off; i != finish; i++) + { + doCanonicalUpdateByte(bytes[i]); + } + } + else + { + sig.BlockUpdate(bytes, off, length); + } + } + + public bool Verify() + { + byte[] trailer = GetSignatureTrailer(); + sig.BlockUpdate(trailer, 0, trailer.Length); + + return sig.VerifySignature(GetSignature()); + } + + private void UpdateWithIdData( + int header, + byte[] idBytes) + { + this.Update( + (byte) header, + (byte)(idBytes.Length >> 24), + (byte)(idBytes.Length >> 16), + (byte)(idBytes.Length >> 8), + (byte)(idBytes.Length)); + this.Update(idBytes); + } + + private void UpdateWithPublicKey( + PgpPublicKey key) + { + byte[] keyBytes = GetEncodedPublicKey(key); + + this.Update( + (byte) 0x99, + (byte)(keyBytes.Length >> 8), + (byte)(keyBytes.Length)); + this.Update(keyBytes); + } + + /// + /// Verify the signature as certifying the passed in public key as associated + /// with the passed in user attributes. + /// + /// User attributes the key was stored under. + /// The key to be verified. + /// True, if the signature matches, false otherwise. + public bool VerifyCertification( + PgpUserAttributeSubpacketVector userAttributes, + PgpPublicKey key) + { + UpdateWithPublicKey(key); + + // + // hash in the userAttributes + // + try + { + MemoryStream bOut = new MemoryStream(); + foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray()) + { + packet.Encode(bOut); + } + UpdateWithIdData(0xd1, bOut.ToArray()); + } + catch (IOException e) + { + throw new PgpException("cannot encode subpacket array", e); + } + + this.Update(sigPck.GetSignatureTrailer()); + + return sig.VerifySignature(this.GetSignature()); + } + + /// + /// Verify the signature as certifying the passed in public key as associated + /// with the passed in ID. + /// + /// ID the key was stored under. + /// The key to be verified. + /// True, if the signature matches, false otherwise. + public bool VerifyCertification( + string id, + PgpPublicKey key) + { + UpdateWithPublicKey(key); + + // + // hash in the id + // + UpdateWithIdData(0xb4, Strings.ToByteArray(id)); + + Update(sigPck.GetSignatureTrailer()); + + return sig.VerifySignature(GetSignature()); + } + + /// Verify a certification for the passed in key against the passed in master key. + /// The key we are verifying against. + /// The key we are verifying. + /// True, if the certification is valid, false otherwise. + public bool VerifyCertification( + PgpPublicKey masterKey, + PgpPublicKey pubKey) + { + UpdateWithPublicKey(masterKey); + UpdateWithPublicKey(pubKey); + + Update(sigPck.GetSignatureTrailer()); + + return sig.VerifySignature(GetSignature()); + } + + /// Verify a key certification, such as revocation, for the passed in key. + /// The key we are checking. + /// True, if the certification is valid, false otherwise. + public bool VerifyCertification( + PgpPublicKey pubKey) + { + if (SignatureType != KeyRevocation + && SignatureType != SubkeyRevocation) + { + throw new InvalidOperationException("signature is not a key signature"); + } + + UpdateWithPublicKey(pubKey); + + Update(sigPck.GetSignatureTrailer()); + + return sig.VerifySignature(GetSignature()); + } + + public int SignatureType + { + get { return sigPck.SignatureType; } + } + + /// The ID of the key that created the signature. + public long KeyId + { + get { return sigPck.KeyId; } + } + + [Obsolete("Use 'CreationTime' property instead")] + public DateTime GetCreationTime() + { + return CreationTime; + } + + /// The creation time of this signature. + public DateTime CreationTime + { + get { return DateTimeUtilities.UnixMsToDateTime(sigPck.CreationTime); } + } + + public byte[] GetSignatureTrailer() + { + return sigPck.GetSignatureTrailer(); + } + + public PgpSignatureSubpacketVector GetHashedSubPackets() + { + return createSubpacketVector(sigPck.GetHashedSubPackets()); + } + + public PgpSignatureSubpacketVector GetUnhashedSubPackets() + { + return createSubpacketVector(sigPck.GetUnhashedSubPackets()); + } + + private PgpSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks) + { + return pcks == null ? null : new PgpSignatureSubpacketVector(pcks); + } + + public byte[] GetSignature() + { + MPInteger[] sigValues = sigPck.GetSignature(); + byte[] signature; + + if (sigValues != null) + { + if (sigValues.Length == 1) // an RSA signature + { + signature = sigValues[0].Value.ToByteArrayUnsigned(); + } + else + { + try + { + signature = new DerSequence( + new DerInteger(sigValues[0].Value), + new DerInteger(sigValues[1].Value)).GetEncoded(); + } + catch (IOException e) + { + throw new PgpException("exception encoding DSA sig.", e); + } + } + } + else + { + signature = sigPck.GetSignatureBytes(); + } + + return signature; + } + + // TODO Handle the encoding stuff by subclassing BcpgObject? + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStream) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStream); + + bcpgOut.WritePacket(sigPck); + + if (trustPck != null) + { + bcpgOut.WritePacket(trustPck); + } + } + + private byte[] GetEncodedPublicKey( + PgpPublicKey pubKey) + { + try + { + return pubKey.publicPk.GetEncodedContents(); + } + catch (IOException e) + { + throw new PgpException("exception preparing key.", e); + } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpSignatureGenerator.cs b/src/core/srcbc/openpgp/PgpSignatureGenerator.cs new file mode 100644 index 0000000..4ea0310 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpSignatureGenerator.cs @@ -0,0 +1,393 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Bcpg.Sig; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Generator for PGP signatures. + // TODO Should be able to implement ISigner? + public class PgpSignatureGenerator + { + private static readonly SignatureSubpacket[] EmptySignatureSubpackets = new SignatureSubpacket[0]; + + private PublicKeyAlgorithmTag keyAlgorithm; + private HashAlgorithmTag hashAlgorithm; + private PgpPrivateKey privKey; + private ISigner sig; + private IDigest dig; + private int signatureType; + private byte lastb; + + private SignatureSubpacket[] unhashed = EmptySignatureSubpackets; + private SignatureSubpacket[] hashed = EmptySignatureSubpackets; + + /// Create a generator for the passed in keyAlgorithm and hashAlgorithm codes. + public PgpSignatureGenerator( + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + + dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm)); + sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm)); + } + + /// Initialise the generator for signing. + public void InitSign( + int sigType, + PgpPrivateKey key) + { + InitSign(sigType, key, null); + } + + /// Initialise the generator for signing. + public void InitSign( + int sigType, + PgpPrivateKey key, + SecureRandom random) + { + this.privKey = key; + this.signatureType = sigType; + + try + { + ICipherParameters cp = key.Key; + if (random != null) + { + cp = new ParametersWithRandom(key.Key, random); + } + + sig.Init(true, cp); + } + catch (InvalidKeyException e) + { + throw new PgpException("invalid key.", e); + } + + dig.Reset(); + lastb = 0; + } + + public void Update( + byte b) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + doCanonicalUpdateByte(b); + } + else + { + doUpdateByte(b); + } + } + + private void doCanonicalUpdateByte( + byte b) + { + if (b == '\r') + { + doUpdateCRLF(); + } + else if (b == '\n') + { + if (lastb != '\r') + { + doUpdateCRLF(); + } + } + else + { + doUpdateByte(b); + } + + lastb = b; + } + + private void doUpdateCRLF() + { + doUpdateByte((byte)'\r'); + doUpdateByte((byte)'\n'); + } + + private void doUpdateByte( + byte b) + { + sig.Update(b); + dig.Update(b); + } + + public void Update( + params byte[] b) + { + Update(b, 0, b.Length); + } + + public void Update( + byte[] b, + int off, + int len) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + doCanonicalUpdateByte(b[i]); + } + } + else + { + sig.BlockUpdate(b, off, len); + dig.BlockUpdate(b, off, len); + } + } + + public void SetHashedSubpackets( + PgpSignatureSubpacketVector hashedPackets) + { + hashed = hashedPackets == null + ? EmptySignatureSubpackets + : hashedPackets.ToSubpacketArray(); + } + + public void SetUnhashedSubpackets( + PgpSignatureSubpacketVector unhashedPackets) + { + unhashed = unhashedPackets == null + ? EmptySignatureSubpackets + : unhashedPackets.ToSubpacketArray(); + } + + /// Return the one pass header associated with the current signature. + public PgpOnePassSignature GenerateOnePassVersion( + bool isNested) + { + return new PgpOnePassSignature( + new OnePassSignaturePacket( + signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested)); + } + + /// Return a signature object containing the current signature state. + public PgpSignature Generate() + { + SignatureSubpacket[] hPkts = hashed, unhPkts = unhashed; + + if (!packetPresent(hashed, SignatureSubpacketTag.CreationTime)) + { + hPkts = insertSubpacket(hPkts, new SignatureCreationTime(false, DateTime.UtcNow)); + } + + if (!packetPresent(hashed, SignatureSubpacketTag.IssuerKeyId) + && !packetPresent(unhashed, SignatureSubpacketTag.IssuerKeyId)) + { + unhPkts = insertSubpacket(unhPkts, new IssuerKeyId(false, privKey.KeyId)); + } + + int version = 4; + byte[] hData; + + try + { + MemoryStream hOut = new MemoryStream(); + + for (int i = 0; i != hPkts.Length; i++) + { + hPkts[i].Encode(hOut); + } + + byte[] data = hOut.ToArray(); + + MemoryStream sOut = new MemoryStream(data.Length + 6); + sOut.WriteByte((byte)version); + sOut.WriteByte((byte)signatureType); + sOut.WriteByte((byte)keyAlgorithm); + sOut.WriteByte((byte)hashAlgorithm); + sOut.WriteByte((byte)(data.Length >> 8)); + sOut.WriteByte((byte)data.Length); + sOut.Write(data, 0, data.Length); + + hData = sOut.ToArray(); + } + catch (IOException e) + { + throw new PgpException("exception encoding hashed data.", e); + } + + sig.BlockUpdate(hData, 0, hData.Length); + dig.BlockUpdate(hData, 0, hData.Length); + + hData = new byte[] + { + (byte) version, + 0xff, + (byte)(hData.Length >> 24), + (byte)(hData.Length >> 16), + (byte)(hData.Length >> 8), + (byte) hData.Length + }; + + sig.BlockUpdate(hData, 0, hData.Length); + dig.BlockUpdate(hData, 0, hData.Length); + + byte[] sigBytes = sig.GenerateSignature(); + byte[] digest = DigestUtilities.DoFinal(dig); + byte[] fingerPrint = new byte[] { digest[0], digest[1] }; + + // an RSA signature + bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign + || keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral; + + MPInteger[] sigValues = isRsa + ? PgpUtilities.RsaSigToMpi(sigBytes) + : PgpUtilities.DsaSigToMpi(sigBytes); + + return new PgpSignature( + new SignaturePacket(signatureType, privKey.KeyId, keyAlgorithm, + hashAlgorithm, hPkts, unhPkts, fingerPrint, sigValues)); + } + + /// Generate a certification for the passed in ID and key. + /// The ID we are certifying against the public key. + /// The key we are certifying against the ID. + /// The certification. + public PgpSignature GenerateCertification( + string id, + PgpPublicKey pubKey) + { + UpdateWithPublicKey(pubKey); + + // + // hash in the id + // + UpdateWithIdData(0xb4, Strings.ToByteArray(id)); + + return Generate(); + } + + /// Generate a certification for the passed in userAttributes. + /// The ID we are certifying against the public key. + /// The key we are certifying against the ID. + /// The certification. + public PgpSignature GenerateCertification( + PgpUserAttributeSubpacketVector userAttributes, + PgpPublicKey pubKey) + { + UpdateWithPublicKey(pubKey); + + // + // hash in the attributes + // + try + { + MemoryStream bOut = new MemoryStream(); + foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray()) + { + packet.Encode(bOut); + } + UpdateWithIdData(0xd1, bOut.ToArray()); + } + catch (IOException e) + { + throw new PgpException("cannot encode subpacket array", e); + } + + return this.Generate(); + } + + /// Generate a certification for the passed in key against the passed in master key. + /// The key we are certifying against. + /// The key we are certifying. + /// The certification. + public PgpSignature GenerateCertification( + PgpPublicKey masterKey, + PgpPublicKey pubKey) + { + UpdateWithPublicKey(masterKey); + UpdateWithPublicKey(pubKey); + + return Generate(); + } + + /// Generate a certification, such as a revocation, for the passed in key. + /// The key we are certifying. + /// The certification. + public PgpSignature GenerateCertification( + PgpPublicKey pubKey) + { + UpdateWithPublicKey(pubKey); + + return Generate(); + } + + private byte[] GetEncodedPublicKey( + PgpPublicKey pubKey) + { + try + { + return pubKey.publicPk.GetEncodedContents(); + } + catch (IOException e) + { + throw new PgpException("exception preparing key.", e); + } + } + + private bool packetPresent( + SignatureSubpacket[] packets, + SignatureSubpacketTag type) + { + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].SubpacketType == type) + { + return true; + } + } + + return false; + } + + private SignatureSubpacket[] insertSubpacket( + SignatureSubpacket[] packets, + SignatureSubpacket subpacket) + { + SignatureSubpacket[] tmp = new SignatureSubpacket[packets.Length + 1]; + tmp[0] = subpacket; + packets.CopyTo(tmp, 1); + return tmp; + } + + private void UpdateWithIdData( + int header, + byte[] idBytes) + { + this.Update( + (byte) header, + (byte)(idBytes.Length >> 24), + (byte)(idBytes.Length >> 16), + (byte)(idBytes.Length >> 8), + (byte)(idBytes.Length)); + this.Update(idBytes); + } + + private void UpdateWithPublicKey( + PgpPublicKey key) + { + byte[] keyBytes = GetEncodedPublicKey(key); + + this.Update( + (byte) 0x99, + (byte)(keyBytes.Length >> 8), + (byte)(keyBytes.Length)); + this.Update(keyBytes); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpSignatureList.cs b/src/core/srcbc/openpgp/PgpSignatureList.cs new file mode 100644 index 0000000..49923ec --- /dev/null +++ b/src/core/srcbc/openpgp/PgpSignatureList.cs @@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// A list of PGP signatures - normally in the signature block after literal data. + public class PgpSignatureList + : PgpObject + { + private PgpSignature[] sigs; + + public PgpSignatureList( + PgpSignature[] sigs) + { + this.sigs = (PgpSignature[]) sigs.Clone(); + } + + public PgpSignatureList( + PgpSignature sig) + { + this.sigs = new PgpSignature[]{ sig }; + } + + public PgpSignature this[int index] + { + get { return sigs[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public PgpSignature Get( + int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return sigs.Length; } + } + + public int Count + { + get { return sigs.Length; } + } + + public bool IsEmpty + { + get { return (sigs.Length == 0); } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpSignatureSubpacketGenerator.cs b/src/core/srcbc/openpgp/PgpSignatureSubpacketGenerator.cs new file mode 100644 index 0000000..740d6ce --- /dev/null +++ b/src/core/srcbc/openpgp/PgpSignatureSubpacketGenerator.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Bcpg.Sig; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Generator for signature subpackets. + public class PgpSignatureSubpacketGenerator + { + private ArrayList list = new ArrayList(); + + public void SetRevocable( + bool isCritical, + bool isRevocable) + { + list.Add(new Revocable(isCritical, isRevocable)); + } + + public void SetExportable( + bool isCritical, + bool isExportable) + { + list.Add(new Exportable(isCritical, isExportable)); + } + + /// + /// Add a TrustSignature packet to the signature. The values for depth and trust are largely + /// installation dependent but there are some guidelines in RFC 4880 - 5.2.3.13. + /// + /// true if the packet is critical. + /// depth level. + /// trust amount. + public void SetTrust( + bool isCritical, + int depth, + int trustAmount) + { + list.Add(new TrustSignature(isCritical, depth, trustAmount)); + } + + /// + /// Set the number of seconds a key is valid for after the time of its creation. + /// A value of zero means the key never expires. + /// + /// True, if should be treated as critical, false otherwise. + /// The number of seconds the key is valid, or zero if no expiry. + public void SetKeyExpirationTime( + bool isCritical, + long seconds) + { + list.Add(new KeyExpirationTime(isCritical, seconds)); + } + + /// + /// Set the number of seconds a signature is valid for after the time of its creation. + /// A value of zero means the signature never expires. + /// + /// True, if should be treated as critical, false otherwise. + /// The number of seconds the signature is valid, or zero if no expiry. + public void SetSignatureExpirationTime( + bool isCritical, + long seconds) + { + list.Add(new SignatureExpirationTime(isCritical, seconds)); + } + + /// + /// Set the creation time for the signature. + ///

    + /// Note: this overrides the generation of a creation time when the signature + /// is generated.

    + ///
    + public void SetSignatureCreationTime( + bool isCritical, + DateTime date) + { + list.Add(new SignatureCreationTime(isCritical, date)); + } + + public void SetPreferredHashAlgorithms( + bool isCritical, + int[] algorithms) + { + list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredHashAlgorithms, isCritical, algorithms)); + } + + public void SetPreferredSymmetricAlgorithms( + bool isCritical, + int[] algorithms) + { + list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredSymmetricAlgorithms, isCritical, algorithms)); + } + + public void SetPreferredCompressionAlgorithms( + bool isCritical, + int[] algorithms) + { + list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredCompressionAlgorithms, isCritical, algorithms)); + } + + public void SetKeyFlags( + bool isCritical, + int flags) + { + list.Add(new KeyFlags(isCritical, flags)); + } + + public void SetSignerUserId( + bool isCritical, + string userId) + { + if (userId == null) + throw new ArgumentNullException("userId"); + + list.Add(new SignerUserId(isCritical, userId)); + } + + public void SetPrimaryUserId( + bool isCritical, + bool isPrimaryUserId) + { + list.Add(new PrimaryUserId(isCritical, isPrimaryUserId)); + } + + public void SetNotationData( + bool isCritical, + bool isHumanReadable, + string notationName, + string notationValue) + { + list.Add(new NotationData(isCritical, isHumanReadable, notationName, notationValue)); + } + + public PgpSignatureSubpacketVector Generate() + { + return new PgpSignatureSubpacketVector( + (SignatureSubpacket[]) list.ToArray(typeof(SignatureSubpacket))); + } + } +} diff --git a/src/core/srcbc/openpgp/PgpSignatureSubpacketVector.cs b/src/core/srcbc/openpgp/PgpSignatureSubpacketVector.cs new file mode 100644 index 0000000..03e493a --- /dev/null +++ b/src/core/srcbc/openpgp/PgpSignatureSubpacketVector.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Bcpg.Sig; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Container for a list of signature subpackets. + public class PgpSignatureSubpacketVector + { + private readonly SignatureSubpacket[] packets; + + internal PgpSignatureSubpacketVector( + SignatureSubpacket[] packets) + { + this.packets = packets; + } + + public SignatureSubpacket GetSubpacket( + SignatureSubpacketTag type) + { + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].SubpacketType == type) + { + return packets[i]; + } + } + + return null; + } + + /** + * Return all signature subpackets of the passed in type. + * @param type subpacket type code + * @return an array of zero or more matching subpackets. + */ + public SignatureSubpacket[] GetSubpackets( + SignatureSubpacketTag type) + { + ArrayList list = new ArrayList(); + + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].SubpacketType == type) + { + list.Add(packets[i]); + } + } + + return (SignatureSubpacket[]) list.ToArray(typeof(SignatureSubpacket)); + } + + public NotationData[] GetNotationDataOccurences() + { + SignatureSubpacket[] notations = GetSubpackets(SignatureSubpacketTag.NotationData); + NotationData[] vals = new NotationData[notations.Length]; + + for (int i = 0; i < notations.Length; i++) + { + vals[i] = (NotationData) notations[i]; + } + + return vals; + } + + public long GetIssuerKeyId() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerKeyId); + + return p == null ? 0 : ((IssuerKeyId) p).KeyId; + } + + public bool HasSignatureCreationTime() + { + return GetSubpacket(SignatureSubpacketTag.CreationTime) != null; + } + + public DateTime GetSignatureCreationTime() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.CreationTime); + + if (p == null) + { + throw new PgpException("SignatureCreationTime not available"); + } + + return ((SignatureCreationTime)p).GetTime(); + } + + /// + /// Return the number of seconds a signature is valid for after its creation date. + /// A value of zero means the signature never expires. + /// + /// Seconds a signature is valid for. + public long GetSignatureExpirationTime() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.ExpireTime); + + return p == null ? 0 : ((SignatureExpirationTime) p).Time; + } + + /// + /// Return the number of seconds a key is valid for after its creation date. + /// A value of zero means the key never expires. + /// + /// Seconds a signature is valid for. + public long GetKeyExpirationTime() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyExpireTime); + + return p == null ? 0 : ((KeyExpirationTime) p).Time; + } + + public int[] GetPreferredHashAlgorithms() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredHashAlgorithms); + + return p == null ? null : ((PreferredAlgorithms) p).GetPreferences(); + } + + public int[] GetPreferredSymmetricAlgorithms() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredSymmetricAlgorithms); + + return p == null ? null : ((PreferredAlgorithms) p).GetPreferences(); + } + + public int[] GetPreferredCompressionAlgorithms() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredCompressionAlgorithms); + + return p == null ? null : ((PreferredAlgorithms) p).GetPreferences(); + } + + public int GetKeyFlags() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyFlags); + + return p == null ? 0 : ((KeyFlags) p).Flags; + } + + public string GetSignerUserId() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.SignerUserId); + + return p == null ? null : ((SignerUserId) p).GetId(); + } + + public SignatureSubpacketTag[] GetCriticalTags() + { + int count = 0; + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].IsCritical()) + { + count++; + } + } + + SignatureSubpacketTag[] list = new SignatureSubpacketTag[count]; + + count = 0; + + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].IsCritical()) + { + list[count++] = packets[i].SubpacketType; + } + } + + return list; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return packets.Length; } + } + + /// Return the number of packets this vector contains. + public int Count + { + get { return packets.Length; } + } + + internal SignatureSubpacket[] ToSubpacketArray() + { + return packets; + } + } +} diff --git a/src/core/srcbc/openpgp/PgpUserAttributeSubpacketVector.cs b/src/core/srcbc/openpgp/PgpUserAttributeSubpacketVector.cs new file mode 100644 index 0000000..02b67d9 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpUserAttributeSubpacketVector.cs @@ -0,0 +1,81 @@ +using Org.BouncyCastle.Bcpg.Attr; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Container for a list of user attribute subpackets. + public class PgpUserAttributeSubpacketVector + { + private readonly UserAttributeSubpacket[] packets; + + internal PgpUserAttributeSubpacketVector( + UserAttributeSubpacket[] packets) + { + this.packets = packets; + } + + public UserAttributeSubpacket GetSubpacket( + UserAttributeSubpacketTag type) + { + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].SubpacketType == type) + { + return packets[i]; + } + } + + return null; + } + + public ImageAttrib GetImageAttribute() + { + UserAttributeSubpacket p = GetSubpacket(UserAttributeSubpacketTag.ImageAttribute); + + return p == null ? null : (ImageAttrib) p; + } + + internal UserAttributeSubpacket[] ToSubpacketArray() + { + return packets; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + PgpUserAttributeSubpacketVector other = obj as PgpUserAttributeSubpacketVector; + + if (other == null) + return false; + + if (other.packets.Length != packets.Length) + { + return false; + } + + for (int i = 0; i != packets.Length; i++) + { + if (!other.packets[i].Equals(packets[i])) + { + return false; + } + } + + return true; + } + + public override int GetHashCode() + { + int code = 0; + + foreach (object o in packets) + { + code ^= o.GetHashCode(); + } + + return code; + } + } +} diff --git a/src/core/srcbc/openpgp/PgpUtilities.cs b/src/core/srcbc/openpgp/PgpUtilities.cs new file mode 100644 index 0000000..557f340 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpUtilities.cs @@ -0,0 +1,431 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Basic utility class. + public sealed class PgpUtilities + { + private PgpUtilities() + { + } + + public static MPInteger[] DsaSigToMpi( + byte[] encoding) + { + DerInteger i1, i2; + + try + { + Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding); + + i1 = (DerInteger) s[0]; + i2 = (DerInteger) s[1]; + } + catch (IOException e) + { + throw new PgpException("exception encoding signature", e); + } + + return new MPInteger[]{ new MPInteger(i1.Value), new MPInteger(i2.Value) }; + } + + public static MPInteger[] RsaSigToMpi( + byte[] encoding) + { + return new MPInteger[]{ new MPInteger(new BigInteger(1, encoding)) }; + } + + public static string GetDigestName( + HashAlgorithmTag hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithmTag.Sha1: + return "SHA1"; + case HashAlgorithmTag.MD2: + return "MD2"; + case HashAlgorithmTag.MD5: + return "MD5"; + case HashAlgorithmTag.RipeMD160: + return "RIPEMD160"; + case HashAlgorithmTag.Sha224: + return "SHA224"; + case HashAlgorithmTag.Sha256: + return "SHA256"; + case HashAlgorithmTag.Sha384: + return "SHA384"; + case HashAlgorithmTag.Sha512: + return "SHA512"; + default: + throw new PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm); + } + } + + public static string GetSignatureName( + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm) + { + string encAlg; + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + encAlg = "RSA"; + break; + case PublicKeyAlgorithmTag.Dsa: + encAlg = "DSA"; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases. + case PublicKeyAlgorithmTag.ElGamalGeneral: + encAlg = "ElGamal"; + break; + default: + throw new PgpException("unknown algorithm tag in signature:" + keyAlgorithm); + } + + return GetDigestName(hashAlgorithm) + "with" + encAlg; + } + + public static string GetSymmetricCipherName( + SymmetricKeyAlgorithmTag algorithm) + { + switch (algorithm) + { + case SymmetricKeyAlgorithmTag.Null: + return null; + case SymmetricKeyAlgorithmTag.TripleDes: + return "DESEDE"; + case SymmetricKeyAlgorithmTag.Idea: + return "IDEA"; + case SymmetricKeyAlgorithmTag.Cast5: + return "CAST5"; + case SymmetricKeyAlgorithmTag.Blowfish: + return "Blowfish"; + case SymmetricKeyAlgorithmTag.Safer: + return "SAFER"; + case SymmetricKeyAlgorithmTag.Des: + return "DES"; + case SymmetricKeyAlgorithmTag.Aes128: + return "AES"; + case SymmetricKeyAlgorithmTag.Aes192: + return "AES"; + case SymmetricKeyAlgorithmTag.Aes256: + return "AES"; + case SymmetricKeyAlgorithmTag.Twofish: + return "Twofish"; + default: + throw new PgpException("unknown symmetric algorithm: " + algorithm); + } + } + + public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) + { + int keySize; + switch (algorithm) + { + case SymmetricKeyAlgorithmTag.Des: + keySize = 64; + break; + case SymmetricKeyAlgorithmTag.Idea: + case SymmetricKeyAlgorithmTag.Cast5: + case SymmetricKeyAlgorithmTag.Blowfish: + case SymmetricKeyAlgorithmTag.Safer: + case SymmetricKeyAlgorithmTag.Aes128: + keySize = 128; + break; + case SymmetricKeyAlgorithmTag.TripleDes: + case SymmetricKeyAlgorithmTag.Aes192: + keySize = 192; + break; + case SymmetricKeyAlgorithmTag.Aes256: + case SymmetricKeyAlgorithmTag.Twofish: + keySize = 256; + break; + default: + throw new PgpException("unknown symmetric algorithm: " + algorithm); + } + + return keySize; + } + + public static KeyParameter MakeKey( + SymmetricKeyAlgorithmTag algorithm, + byte[] keyBytes) + { + string algName = GetSymmetricCipherName(algorithm); + + return ParameterUtilities.CreateKeyParameter(algName, keyBytes); + } + + public static KeyParameter MakeRandomKey( + SymmetricKeyAlgorithmTag algorithm, + SecureRandom random) + { + int keySize = GetKeySize(algorithm); + byte[] keyBytes = new byte[(keySize + 7) / 8]; + random.NextBytes(keyBytes); + return MakeKey(algorithm, keyBytes); + } + + public static KeyParameter MakeKeyFromPassPhrase( + SymmetricKeyAlgorithmTag algorithm, + S2k s2k, + char[] passPhrase) + { + int keySize = GetKeySize(algorithm); + byte[] pBytes = Strings.ToByteArray(new string(passPhrase)); + byte[] keyBytes = new byte[(keySize + 7) / 8]; + + int generatedBytes = 0; + int loopCount = 0; + + while (generatedBytes < keyBytes.Length) + { + IDigest digest; + if (s2k != null) + { + try + { + switch (s2k.HashAlgorithm) + { + case HashAlgorithmTag.Sha1: + digest = DigestUtilities.GetDigest("SHA1"); + break; + default: + throw new PgpException("unknown hash algorithm: " + s2k.HashAlgorithm); + } + } + catch (Exception e) + { + throw new PgpException("can't find S2k digest", e); + } + + for (int i = 0; i != loopCount; i++) + { + digest.Update(0); + } + + byte[] iv = s2k.GetIV(); + + switch (s2k.Type) + { + case S2k.Simple: + digest.BlockUpdate(pBytes, 0, pBytes.Length); + break; + case S2k.Salted: + digest.BlockUpdate(iv, 0, iv.Length); + digest.BlockUpdate(pBytes, 0, pBytes.Length); + break; + case S2k.SaltedAndIterated: + long count = s2k.IterationCount; + digest.BlockUpdate(iv, 0, iv.Length); + digest.BlockUpdate(pBytes, 0, pBytes.Length); + + count -= iv.Length + pBytes.Length; + + while (count > 0) + { + if (count < iv.Length) + { + digest.BlockUpdate(iv, 0, (int)count); + break; + } + else + { + digest.BlockUpdate(iv, 0, iv.Length); + count -= iv.Length; + } + + if (count < pBytes.Length) + { + digest.BlockUpdate(pBytes, 0, (int)count); + count = 0; + } + else + { + digest.BlockUpdate(pBytes, 0, pBytes.Length); + count -= pBytes.Length; + } + } + break; + default: + throw new PgpException("unknown S2k type: " + s2k.Type); + } + } + else + { + try + { + digest = DigestUtilities.GetDigest("MD5"); + + for (int i = 0; i != loopCount; i++) + { + digest.Update(0); + } + + digest.BlockUpdate(pBytes, 0, pBytes.Length); + } + catch (Exception e) + { + throw new PgpException("can't find MD5 digest", e); + } + } + + byte[] dig = DigestUtilities.DoFinal(digest); + + if (dig.Length > (keyBytes.Length - generatedBytes)) + { + Array.Copy(dig, 0, keyBytes, generatedBytes, keyBytes.Length - generatedBytes); + } + else + { + Array.Copy(dig, 0, keyBytes, generatedBytes, dig.Length); + } + + generatedBytes += dig.Length; + + loopCount++; + } + + Array.Clear(pBytes, 0, pBytes.Length); + + return MakeKey(algorithm, keyBytes); + } + + /// Write out the passed in file as a literal data packet. + public static void WriteFileToLiteralData( + Stream outputStream, + char fileType, + FileInfo file) + { + Stream inStr = file.OpenRead(); + Stream outStr = new PgpLiteralDataGenerator().Open( + outputStream, fileType, file.Name, file.Length, file.LastWriteTime); + + Streams.PipeAll(inStr, outStr); + + inStr.Close(); + outStr.Close(); + } + + /// Write out the passed in file as a literal data packet in partial packet format. + public static void WriteFileToLiteralData( + Stream outputStream, + char fileType, + FileInfo file, + byte[] buffer) + { + PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator(); + Stream pOut = lData.Open(outputStream, fileType, file.Name, file.LastWriteTime, buffer); + FileStream inputStream = file.OpenRead(); + byte[] buf = new byte[buffer.Length]; + + int len; + while ((len = inputStream.Read(buf, 0, buf.Length)) > 0) + { + pOut.Write(buf, 0, len); + } + + lData.Close(); + inputStream.Close(); + } + + private const int ReadAhead = 60; + + private static bool IsPossiblyBase64( + int ch) + { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/') + || (ch == '\r') || (ch == '\n'); + } + + /// + /// Return either an ArmoredInputStream or a BcpgInputStream based on whether + /// the initial characters of the stream are binary PGP encodings or not. + /// + public static Stream GetDecoderStream( + Stream inputStream) + { + // TODO Remove this restriction? + if (!inputStream.CanSeek) + throw new ArgumentException("inputStream must be seek-able", "inputStream"); + + long markedPos = inputStream.Position; + + int ch = inputStream.ReadByte(); + if ((ch & 0x80) != 0) + { + inputStream.Position = markedPos; + + return inputStream; + } + else + { + if (!IsPossiblyBase64(ch)) + { + inputStream.Position = markedPos; + + return new ArmoredInputStream(inputStream); + } + + byte[] buf = new byte[ReadAhead]; + int count = 1; + int index = 1; + + buf[0] = (byte)ch; + while (count != ReadAhead && (ch = inputStream.ReadByte()) >= 0) + { + if (!IsPossiblyBase64(ch)) + { + inputStream.Position = markedPos; + + return new ArmoredInputStream(inputStream); + } + + if (ch != '\n' && ch != '\r') + { + buf[index++] = (byte)ch; + } + + count++; + } + + inputStream.Position = markedPos; + + // + // nothing but new lines, little else, assume regular armoring + // + if (count < 4) + { + return new ArmoredInputStream(inputStream); + } + + // + // test our non-blank data + // + byte[] firstBlock = new byte[8]; + Array.Copy(buf, 0, firstBlock, 0, firstBlock.Length); + byte[] decoded = Base64.Decode(firstBlock); + + // + // it's a base64 PGP block. + // + bool hasHeaders = (decoded[0] & 0x80) == 0; + + return new ArmoredInputStream(inputStream, hasHeaders); + } + } + } +} diff --git a/src/core/srcbc/openpgp/PgpV3SignatureGenerator.cs b/src/core/srcbc/openpgp/PgpV3SignatureGenerator.cs new file mode 100644 index 0000000..638e406 --- /dev/null +++ b/src/core/srcbc/openpgp/PgpV3SignatureGenerator.cs @@ -0,0 +1,199 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// Generator for old style PGP V3 Signatures. + // TODO Should be able to implement ISigner? + public class PgpV3SignatureGenerator + { + private PublicKeyAlgorithmTag keyAlgorithm; + private HashAlgorithmTag hashAlgorithm; + private PgpPrivateKey privKey; + private ISigner sig; + private IDigest dig; + private int signatureType; + private byte lastb; + + /// Create a generator for the passed in keyAlgorithm and hashAlgorithm codes. + public PgpV3SignatureGenerator( + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + + dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm)); + sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm)); + } + + /// Initialise the generator for signing. + public void InitSign( + int sigType, + PgpPrivateKey key) + { + InitSign(sigType, key, null); + } + + /// Initialise the generator for signing. + public void InitSign( + int sigType, + PgpPrivateKey key, + SecureRandom random) + { + this.privKey = key; + this.signatureType = sigType; + + try + { + ICipherParameters cp = key.Key; + if (random != null) + { + cp = new ParametersWithRandom(key.Key, random); + } + + sig.Init(true, cp); + } + catch (InvalidKeyException e) + { + throw new PgpException("invalid key.", e); + } + + dig.Reset(); + lastb = 0; + } + + public void Update( + byte b) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + doCanonicalUpdateByte(b); + } + else + { + doUpdateByte(b); + } + } + + private void doCanonicalUpdateByte( + byte b) + { + if (b == '\r') + { + doUpdateCRLF(); + } + else if (b == '\n') + { + if (lastb != '\r') + { + doUpdateCRLF(); + } + } + else + { + doUpdateByte(b); + } + + lastb = b; + } + + private void doUpdateCRLF() + { + doUpdateByte((byte)'\r'); + doUpdateByte((byte)'\n'); + } + + private void doUpdateByte( + byte b) + { + sig.Update(b); + dig.Update(b); + } + + public void Update( + byte[] b) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + for (int i = 0; i != b.Length; i++) + { + doCanonicalUpdateByte(b[i]); + } + } + else + { + sig.BlockUpdate(b, 0, b.Length); + dig.BlockUpdate(b, 0, b.Length); + } + } + + public void Update( + byte[] b, + int off, + int len) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + doCanonicalUpdateByte(b[i]); + } + } + else + { + sig.BlockUpdate(b, off, len); + dig.BlockUpdate(b, off, len); + } + } + + /// Return the one pass header associated with the current signature. + public PgpOnePassSignature GenerateOnePassVersion( + bool isNested) + { + return new PgpOnePassSignature( + new OnePassSignaturePacket(signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested)); + } + + /// Return a V3 signature object containing the current signature state. + public PgpSignature Generate() + { + long creationTime = DateTimeUtilities.CurrentUnixMs() / 1000L; + + byte[] hData = new byte[] + { + (byte) signatureType, + (byte)(creationTime >> 24), + (byte)(creationTime >> 16), + (byte)(creationTime >> 8), + (byte) creationTime + }; + + sig.BlockUpdate(hData, 0, hData.Length); + dig.BlockUpdate(hData, 0, hData.Length); + + byte[] sigBytes = sig.GenerateSignature(); + byte[] digest = DigestUtilities.DoFinal(dig); + byte[] fingerPrint = new byte[]{ digest[0], digest[1] }; + + // an RSA signature + bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign + || keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral; + + MPInteger[] sigValues = isRsa + ? PgpUtilities.RsaSigToMpi(sigBytes) + : PgpUtilities.DsaSigToMpi(sigBytes); + + return new PgpSignature( + new SignaturePacket(3, signatureType, privKey.KeyId, keyAlgorithm, + hashAlgorithm, creationTime * 1000L, fingerPrint, sigValues)); + } + } +} diff --git a/src/core/srcbc/openpgp/WrappedGeneratorStream.cs b/src/core/srcbc/openpgp/WrappedGeneratorStream.cs new file mode 100644 index 0000000..e550ffd --- /dev/null +++ b/src/core/srcbc/openpgp/WrappedGeneratorStream.cs @@ -0,0 +1,25 @@ +using System.IO; + +using Org.BouncyCastle.Asn1.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public class WrappedGeneratorStream + : FilterStream + { + private readonly IStreamGenerator gen; + + public WrappedGeneratorStream( + IStreamGenerator gen, + Stream str) + : base(str) + { + this.gen = gen; + } + + public override void Close() + { + gen.Close(); + } + } +} diff --git a/src/core/srcbc/openssl/IPasswordFinder.cs b/src/core/srcbc/openssl/IPasswordFinder.cs new file mode 100644 index 0000000..d32d3fb --- /dev/null +++ b/src/core/srcbc/openssl/IPasswordFinder.cs @@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.OpenSsl +{ + public interface IPasswordFinder + { + char[] GetPassword(); + } +} diff --git a/src/core/srcbc/openssl/PEMReader.cs b/src/core/srcbc/openssl/PEMReader.cs new file mode 100644 index 0000000..982fc6f --- /dev/null +++ b/src/core/srcbc/openssl/PEMReader.cs @@ -0,0 +1,453 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.OpenSsl +{ + /** + * Class for reading OpenSSL PEM encoded streams containing + * X509 certificates, PKCS8 encoded keys and PKCS7 objects. + *

    + * In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Keys and + * Certificates will be returned using the appropriate java.security type.

    + */ + public class PemReader + { + private readonly TextReader reader; + private readonly IPasswordFinder pFinder; + + public TextReader Reader + { + get { return reader; } + } + + /** + * Create a new PemReader + * + * @param reader the Reader + */ + public PemReader( + TextReader reader) + : this(reader, null) + { + } + + /** + * Create a new PemReader with a password finder + * + * @param reader the Reader + * @param pFinder the password finder + */ + public PemReader( + TextReader reader, + IPasswordFinder pFinder) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + this.reader = reader; + this.pFinder = pFinder; + } + + private const string BeginString = "-----BEGIN "; + + public object ReadObject() + { + string line; + while ((line = reader.ReadLine()) != null) + { + int startPos = line.IndexOf(BeginString); + if (startPos == -1) + continue; + + startPos += BeginString.Length; + + int endPos = line.IndexOf('-', startPos); + if (endPos == -1) + endPos = line.Length; + + string headerName = line.Substring(startPos, endPos - startPos).Trim(); + //Console.WriteLine("[" + headerName + "]"); + + string endMarker = "-----END " + headerName; + + switch (headerName) + { + case "PUBLIC KEY": + return ReadPublicKey(endMarker); + case "RSA PUBLIC KEY": + return ReadRsaPublicKey(endMarker); + case "CERTIFICATE REQUEST": + case "NEW CERTIFICATE REQUEST": + return ReadCertificateRequest(endMarker); + case "CERTIFICATE": + case "X509 CERTIFICATE": + return ReadCertificate(endMarker); + case "PKCS7": + return ReadPkcs7(endMarker); + case "X509 CRL": + return ReadCrl(endMarker); + case "ATTRIBUTE CERTIFICATE": + return ReadAttributeCertificate(endMarker); + case "RSA PRIVATE KEY": + return ReadKeyPair("RSA", endMarker); + case "DSA PRIVATE KEY": + return ReadKeyPair("DSA", endMarker); + // TODO Add back in when tests done, and return type issue resolved + //case "EC PARAMETERS": + // return ReadECParameters(endMarker); + case "EC PRIVATE KEY": + return ReadECPrivateKey(endMarker); + default: + // TODO Throw an exception for an unknown header? + + // Ignore contents + ReadBytes(endMarker); + break; + } + } + + return null; + } + + private byte[] ReadBytes( + string endMarker) + { + return ReadBytesAndFields(endMarker, null); + } + + private byte[] ReadBytesAndFields( + string endMarker, + IDictionary fields) + { + StringBuilder buf = new StringBuilder(); + + string line; + while ((line = reader.ReadLine()) != null + && line.IndexOf(endMarker) == -1) + { + int colonPos = line.IndexOf(':'); + + if (colonPos == -1) + { + buf.Append(line.Trim()); + } + else if (fields != null) + { + // Process field + string fieldName = line.Substring(0, colonPos).Trim(); + + if (fieldName.StartsWith("X-")) + fieldName = fieldName.Substring(2); + + string fieldValue = line.Substring(colonPos + 1).Trim(); + + // TODO Complain if field already specified? + fields[fieldName] = fieldValue; + } + } + + if (line == null) + { + throw new IOException(endMarker + " not found"); + } + + if (buf.Length % 4 != 0) + { + throw new IOException("base64 data appears to be truncated"); + } + + return Base64.Decode(buf.ToString()); + } + + private AsymmetricKeyParameter ReadRsaPublicKey( + string endMarker) + { + RsaPublicKeyStructure rsaPubStructure = RsaPublicKeyStructure.GetInstance( + Asn1Object.FromByteArray( + ReadBytes(endMarker))); + + return new RsaKeyParameters( + false, // not private + rsaPubStructure.Modulus, + rsaPubStructure.PublicExponent); + } + + private AsymmetricKeyParameter ReadPublicKey( + string endMarker) + { + return PublicKeyFactory.CreateKey( + ReadBytes(endMarker)); + } + + /** + * Reads in a X509Certificate. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + private X509Certificate ReadCertificate( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + try + { + return new X509CertificateParser().ReadCertificate(bytes); + } + catch (Exception e) + { + throw new IOException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a X509CRL. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + private X509Crl ReadCrl( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + try + { + return new X509CrlParser().ReadCrl(bytes); + } + catch (Exception e) + { + throw new IOException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a PKCS10 certification request. + * + * @return the certificate request. + * @throws IOException if an I/O error occured + */ + private Pkcs10CertificationRequest ReadCertificateRequest( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + try + { + return new Pkcs10CertificationRequest(bytes); + } + catch (Exception e) + { + throw new IOException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a X509 Attribute Certificate. + * + * @return the X509 Attribute Certificate + * @throws IOException if an I/O error occured + */ + private IX509AttributeCertificate ReadAttributeCertificate( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + return new X509V2AttributeCertificate(bytes); + } + + /** + * Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS + * API. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + // TODO Consider returning Asn1.Pkcs.ContentInfo + private Asn1.Cms.ContentInfo ReadPkcs7( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + + try + { + return Asn1.Cms.ContentInfo.GetInstance( + Asn1Object.FromByteArray(bytes)); + } + catch (Exception e) + { + throw new IOException("problem parsing PKCS7 object: " + e.ToString()); + } + } + + /** + * Read a Key Pair + */ + private AsymmetricCipherKeyPair ReadKeyPair( + string type, + string endMarker) + { + // + // extract the key + // + IDictionary fields = new Hashtable(); + byte[] keyBytes = ReadBytesAndFields(endMarker, fields); + + string procType = (string) fields["Proc-Type"]; + + if (procType == "4,ENCRYPTED") + { + if (pFinder == null) + throw new InvalidOperationException("No password finder specified, but a password is required"); + + char[] password = pFinder.GetPassword(); + + if (password == null) + throw new IOException("Password is null, but a password is required"); + + string dekInfo = (string) fields["DEK-Info"]; + string[] tknz = dekInfo.Split(','); + + string dekAlgName = tknz[0].Trim(); + byte[] iv = Hex.Decode(tknz[1].Trim()); + + keyBytes = PemUtilities.Crypt(false, keyBytes, password, dekAlgName, iv); + } + + try + { + AsymmetricKeyParameter pubSpec, privSpec; + Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(keyBytes); + + switch (type) + { + case "RSA": + { + RsaPrivateKeyStructure rsa = new RsaPrivateKeyStructure(seq); + + pubSpec = new RsaKeyParameters(false, rsa.Modulus, rsa.PublicExponent); + privSpec = new RsaPrivateCrtKeyParameters( + rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, + rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, + rsa.Coefficient); + + break; + } + + case "DSA": + { + // TODO Create an ASN1 object somewhere for this? + //DerInteger v = (DerInteger)seq[0]; + DerInteger p = (DerInteger)seq[1]; + DerInteger q = (DerInteger)seq[2]; + DerInteger g = (DerInteger)seq[3]; + DerInteger y = (DerInteger)seq[4]; + DerInteger x = (DerInteger)seq[5]; + + DsaParameters parameters = new DsaParameters(p.Value, q.Value, g.Value); + + privSpec = new DsaPrivateKeyParameters(x.Value, parameters); + pubSpec = new DsaPublicKeyParameters(y.Value, parameters); + + break; + } + + default: + throw new ArgumentException("Unknown key type: " + type, "type"); + } + + return new AsymmetricCipherKeyPair(pubSpec, privSpec); + } + catch (Exception e) + { + throw new IOException( + "problem creating " + type + " private key: " + e.ToString()); + } + } + + // TODO Add an equivalent class for ECNamedCurveParameterSpec? + //private ECNamedCurveParameterSpec ReadECParameters( + private X9ECParameters ReadECParameters( + string endMarker) + { + byte[] bytes = ReadBytes(endMarker); + DerObjectIdentifier oid = (DerObjectIdentifier) Asn1Object.FromByteArray(bytes); + + //return ECNamedCurveTable.getParameterSpec(oid.Id); + return GetCurveParameters(oid.Id); + } + + //private static ECDomainParameters GetCurveParameters( + private static X9ECParameters GetCurveParameters( + string name) + { + // TODO ECGost3410NamedCurves support (returns ECDomainParameters though) + X9ECParameters ecP = X962NamedCurves.GetByName(name); + + if (ecP == null) + { + ecP = SecNamedCurves.GetByName(name); + if (ecP == null) + { + ecP = NistNamedCurves.GetByName(name); + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.GetByName(name); + + if (ecP == null) + throw new Exception("unknown curve name: " + name); + } + } + } + + //return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + return ecP; + } + + private AsymmetricCipherKeyPair ReadECPrivateKey( + string endMarker) + { + try + { + byte[] bytes = ReadBytes(endMarker); + ECPrivateKeyStructure pKey = new ECPrivateKeyStructure( + (Asn1Sequence) Asn1Object.FromByteArray(bytes)); + AlgorithmIdentifier algId = new AlgorithmIdentifier( + X9ObjectIdentifiers.IdECPublicKey, pKey.GetParameters()); + + PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey.ToAsn1Object()); + SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.GetPublicKey().GetBytes()); + + // TODO Are the keys returned here ECDSA, as Java version forces? + return new AsymmetricCipherKeyPair( + PublicKeyFactory.CreateKey(pubInfo), + PrivateKeyFactory.CreateKey(privInfo)); + } + catch (InvalidCastException e) + { + throw new IOException("wrong ASN.1 object found in stream.", e); + } + catch (Exception e) + { + throw new IOException("problem parsing EC private key.", e); + } + } + } +} diff --git a/src/core/srcbc/openssl/PEMUtilities.cs b/src/core/srcbc/openssl/PEMUtilities.cs new file mode 100644 index 0000000..e9671eb --- /dev/null +++ b/src/core/srcbc/openssl/PEMUtilities.cs @@ -0,0 +1,138 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.OpenSsl +{ + internal sealed class PemUtilities + { + internal static bool ParseDekAlgName( + string dekAlgName, + out string baseAlg, + out string mode) + { + baseAlg = dekAlgName; + mode = "ECB"; + + if (dekAlgName == "DES-EDE" || dekAlgName == "DES-EDE3") + return true; + + int pos = dekAlgName.LastIndexOf('-'); + if (pos < 0) + return false; + + baseAlg = dekAlgName.Substring(0, pos); + mode = dekAlgName.Substring(pos + 1); + + return true; + } + + internal static byte[] Crypt( + bool encrypt, + byte[] bytes, + char[] password, + string dekAlgName, + byte[] iv) + { + string baseAlg, mode; + if (!ParseDekAlgName(dekAlgName, out baseAlg, out mode)) + throw new ArgumentException("Unknown DEK algorithm: " + dekAlgName, "dekAlgName"); + + string padding; + switch (mode) + { + case "CBC": + case "ECB": + padding = "PKCS5Padding"; + break; + case "CFB": + case "OFB": + padding = "NoPadding"; + break; + default: + throw new ArgumentException("Unknown DEK algorithm: " + dekAlgName, "dekAlgName"); + } + + string algorithm; + + byte[] salt = iv; + switch (baseAlg) + { + case "AES-128": + case "AES-192": + case "AES-256": + algorithm = "AES"; + if (salt.Length > 8) + { + salt = new byte[8]; + Array.Copy(iv, 0, salt, 0, salt.Length); + } + break; + case "BF": + algorithm = "BLOWFISH"; + break; + case "DES": + algorithm = "DES"; + break; + case "DES-EDE": + case "DES-EDE3": + algorithm = "DESede"; + break; + case "RC2": + case "RC2-40": + case "RC2-64": + algorithm = "RC2"; + break; + default: + throw new ArgumentException("Unknown DEK algorithm: " + dekAlgName, "dekAlgName"); + } + + string cipherName = algorithm + "/" + mode + "/" + padding; + IBufferedCipher cipher = CipherUtilities.GetCipher(cipherName); + + ICipherParameters cParams = GetCipherParameters(password, baseAlg, salt); + + if (mode != "ECB") + { + cParams = new ParametersWithIV(cParams, iv); + } + + cipher.Init(encrypt, cParams); + + return cipher.DoFinal(bytes); + } + + private static ICipherParameters GetCipherParameters( + char[] password, + string baseAlg, + byte[] salt) + { + string algorithm; + int keyBits; + switch (baseAlg) + { + case "AES-128": keyBits = 128; algorithm = "AES128"; break; + case "AES-192": keyBits = 192; algorithm = "AES192"; break; + case "AES-256": keyBits = 256; algorithm = "AES256"; break; + case "BF": keyBits = 128; algorithm = "BLOWFISH"; break; + case "DES": keyBits = 64; algorithm = "DES"; break; + case "DES-EDE": keyBits = 128; algorithm = "DESEDE"; break; + case "DES-EDE3": keyBits = 192; algorithm = "DESEDE3"; break; + case "RC2": keyBits = 128; algorithm = "RC2"; break; + case "RC2-40": keyBits = 40; algorithm = "RC2"; break; + case "RC2-64": keyBits = 64; algorithm = "RC2"; break; + default: + return null; + } + + OpenSslPbeParametersGenerator pGen = new OpenSslPbeParametersGenerator(); + + pGen.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password), salt); + + return pGen.GenerateDerivedParameters(algorithm, keyBits); + } + } +} diff --git a/src/core/srcbc/openssl/PEMWriter.cs b/src/core/srcbc/openssl/PEMWriter.cs new file mode 100644 index 0000000..e6fa7cb --- /dev/null +++ b/src/core/srcbc/openssl/PEMWriter.cs @@ -0,0 +1,278 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.OpenSsl +{ + /// General purpose writer for OpenSSL PEM objects. + public class PemWriter + { + private readonly TextWriter writer; + + public TextWriter Writer + { + get { return writer; } + } + + /// The TextWriter object to write the output to. + public PemWriter( + TextWriter writer) + { + if (writer == null) + throw new ArgumentNullException("writer"); + + this.writer = writer; + } + + public void WriteObject( + object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + string type; + byte[] encoding; + + if (obj is X509Certificate) + { + // TODO Should we prefer "X509 CERTIFICATE" here? + type = "CERTIFICATE"; + try + { + encoding = ((X509Certificate)obj).GetEncoded(); + } + catch (CertificateEncodingException e) + { + throw new IOException("Cannot Encode object: " + e.ToString()); + } + } + else if (obj is X509Crl) + { + type = "X509 CRL"; + try + { + encoding = ((X509Crl)obj).GetEncoded(); + } + catch (CrlException e) + { + throw new IOException("Cannot Encode object: " + e.ToString()); + } + } + else if (obj is AsymmetricCipherKeyPair) + { + WriteObject(((AsymmetricCipherKeyPair)obj).Private); + return; + } + else if (obj is AsymmetricKeyParameter) + { + AsymmetricKeyParameter akp = (AsymmetricKeyParameter) obj; + if (akp.IsPrivate) + { + string keyType; + encoding = EncodePrivateKey(akp, out keyType); + + type = keyType + " PRIVATE KEY"; + } + else + { + type = "PUBLIC KEY"; + + encoding = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(akp).GetDerEncoded(); + } + } + else if (obj is IX509AttributeCertificate) + { + type = "ATTRIBUTE CERTIFICATE"; + encoding = ((X509V2AttributeCertificate)obj).GetEncoded(); + } + else if (obj is Pkcs10CertificationRequest) + { + type = "CERTIFICATE REQUEST"; + encoding = ((Pkcs10CertificationRequest)obj).GetEncoded(); + } + else if (obj is Asn1.Cms.ContentInfo) + { + type = "PKCS7"; + encoding = ((Asn1.Cms.ContentInfo)obj).GetEncoded(); + } + else + { + throw new ArgumentException("Object type not supported: " + obj.GetType().FullName, "obj"); + } + + WritePemBlock(type, encoding); + } + + public void WriteObject( + object obj, + string algorithm, + char[] password, + SecureRandom random) + { + if (obj == null) + throw new ArgumentNullException("obj"); + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (password == null) + throw new ArgumentNullException("password"); + if (random == null) + throw new ArgumentNullException("random"); + + if (obj is AsymmetricCipherKeyPair) + { + WriteObject(((AsymmetricCipherKeyPair) obj).Private, algorithm, password, random); + return; + } + + string type = null; + byte[] keyData = null; + + if (obj is AsymmetricKeyParameter) + { + AsymmetricKeyParameter akp = (AsymmetricKeyParameter) obj; + if (akp.IsPrivate) + { + string keyType; + keyData = EncodePrivateKey(akp, out keyType); + + type = keyType + " PRIVATE KEY"; + } + } + + if (type == null || keyData == null) + { + // TODO Support other types? + throw new ArgumentException("Object type not supported: " + obj.GetType().FullName, "obj"); + } + + + string dekAlgName = algorithm.ToUpper(CultureInfo.InvariantCulture); + + // Note: For backward compatibility + if (dekAlgName == "DESEDE") + { + dekAlgName = "DES-EDE3-CBC"; + } + + int ivLength = dekAlgName.StartsWith("AES-") ? 16 : 8; + + byte[] iv = new byte[ivLength]; + random.NextBytes(iv); + + byte[] encData = PemUtilities.Crypt(true, keyData, password, dekAlgName, iv); + byte[] hexIV = Hex.Encode(iv); + + WritePemBlock(type, encData, + "Proc-Type: 4,ENCRYPTED", + "DEK-Info: " + dekAlgName + "," + Encoding.ASCII.GetString(hexIV, 0, hexIV.Length)); + } + + private byte[] EncodePrivateKey( + AsymmetricKeyParameter akp, + out string keyType) + { + PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp); + + DerObjectIdentifier oid = info.AlgorithmID.ObjectID; + + if (oid.Equals(X9ObjectIdentifiers.IdDsa)) + { + keyType = "DSA"; + + DsaParameter p = DsaParameter.GetInstance(info.AlgorithmID.Parameters); + + BigInteger x = ((DsaPrivateKeyParameters) akp).X; + BigInteger y = p.G.ModPow(x, p.P); + + // TODO Create an ASN1 object somewhere for this? + return new DerSequence( + new DerInteger(0), + new DerInteger(p.P), + new DerInteger(p.Q), + new DerInteger(p.G), + new DerInteger(y), + new DerInteger(x)).GetEncoded(); + } + + if (oid.Equals(PkcsObjectIdentifiers.RsaEncryption)) + { + keyType = "RSA"; + return info.PrivateKey.GetEncoded(); + } + + throw new ArgumentException("Cannot handle private key of type: " + akp.GetType().FullName, "akp"); + } + + private void WritePemBlock( + string type, + byte[] data, + params string[] fields) + { + WriteHeader(type); + + if (fields.Length > 0) + { + foreach (string field in fields) + { + writer.WriteLine(field); + } + + writer.WriteLine(); + } + + WriteBytes(Base64.Encode(data)); + + WriteFooter(type); + } + + private void WriteHeader( + string type) + { + writer.WriteLine("-----BEGIN " + type + "-----"); + } + + private void WriteFooter( + string type) + { + writer.WriteLine("-----END " + type + "-----"); + } + + private const int LineLength = 64; + + private void WriteBytes( + byte[] bytes) + { + int pos = 0; + int remaining = bytes.Length; + char[] buf = new char[LineLength]; + + while (remaining > LineLength) + { + Encoding.ASCII.GetChars(bytes, pos, LineLength, buf, 0); + writer.WriteLine(buf); + + pos += LineLength; + remaining -= LineLength; + } + + Encoding.ASCII.GetChars(bytes, pos, remaining, buf, 0); + writer.WriteLine(buf, 0, remaining); + } + } +} diff --git a/src/core/srcbc/pkcs/AsymmetricKeyEntry.cs b/src/core/srcbc/pkcs/AsymmetricKeyEntry.cs new file mode 100644 index 0000000..6dd2c0b --- /dev/null +++ b/src/core/srcbc/pkcs/AsymmetricKeyEntry.cs @@ -0,0 +1,32 @@ +using System.Collections; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Pkcs +{ + public class AsymmetricKeyEntry + : Pkcs12Entry + { + private readonly AsymmetricKeyParameter key; + + public AsymmetricKeyEntry( + AsymmetricKeyParameter key) + : base(new Hashtable()) + { + this.key = key; + } + + public AsymmetricKeyEntry( + AsymmetricKeyParameter key, + Hashtable attributes) + : base(attributes) + { + this.key = key; + } + + public AsymmetricKeyParameter Key + { + get { return this.key; } + } + } +} diff --git a/src/core/srcbc/pkcs/EncryptedPrivateKeyInfoFactory.cs b/src/core/srcbc/pkcs/EncryptedPrivateKeyInfoFactory.cs new file mode 100644 index 0000000..67fcee3 --- /dev/null +++ b/src/core/srcbc/pkcs/EncryptedPrivateKeyInfoFactory.cs @@ -0,0 +1,75 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Pkcs +{ + public sealed class EncryptedPrivateKeyInfoFactory + { + private EncryptedPrivateKeyInfoFactory() + { + } + + public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo( + DerObjectIdentifier algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + AsymmetricKeyParameter key) + { + return CreateEncryptedPrivateKeyInfo( + algorithm.Id, passPhrase, salt, iterationCount, + PrivateKeyInfoFactory.CreatePrivateKeyInfo(key)); + } + + public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo( + string algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + AsymmetricKeyParameter key) + { + return CreateEncryptedPrivateKeyInfo( + algorithm, passPhrase, salt, iterationCount, + PrivateKeyInfoFactory.CreatePrivateKeyInfo(key)); + } + + public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo( + string algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + PrivateKeyInfo keyInfo) + { + if (!PbeUtilities.IsPbeAlgorithm(algorithm)) + throw new ArgumentException("attempt to use non-Pbe algorithm with Pbe EncryptedPrivateKeyInfo generation"); + + IBufferedCipher cipher = PbeUtilities.CreateEngine(algorithm) as IBufferedCipher; + + if (cipher == null) + { + // TODO Throw exception? + } + + Asn1Encodable parameters = PbeUtilities.GenerateAlgorithmParameters( + algorithm, salt, iterationCount); + + ICipherParameters keyParameters = PbeUtilities.GenerateCipherParameters( + algorithm, passPhrase, parameters); + + cipher.Init(true, keyParameters); + + byte[] keyBytes = keyInfo.GetEncoded(); + byte[] encoding = cipher.DoFinal(keyBytes); + + DerObjectIdentifier oid = PbeUtilities.GetObjectIdentifier(algorithm); + AlgorithmIdentifier algID = new AlgorithmIdentifier(oid, parameters); + + return new EncryptedPrivateKeyInfo(algID, encoding); + } + } +} diff --git a/src/core/srcbc/pkcs/Pkcs10CertificationRequest.cs b/src/core/srcbc/pkcs/Pkcs10CertificationRequest.cs new file mode 100644 index 0000000..9275d2e --- /dev/null +++ b/src/core/srcbc/pkcs/Pkcs10CertificationRequest.cs @@ -0,0 +1,447 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkcs +{ + /// + /// A class for verifying and creating Pkcs10 Certification requests. + /// + /// + /// CertificationRequest ::= Sequence { + /// certificationRequestInfo CertificationRequestInfo, + /// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, + /// signature BIT STRING + /// } + /// + /// CertificationRequestInfo ::= Sequence { + /// version Integer { v1(0) } (v1,...), + /// subject Name, + /// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, + /// attributes [0] Attributes{{ CRIAttributes }} + /// } + /// + /// Attributes { ATTRIBUTE:IOSet } ::= Set OF Attr{{ IOSet }} + /// + /// Attr { ATTRIBUTE:IOSet } ::= Sequence { + /// type ATTRIBUTE.&id({IOSet}), + /// values Set SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type}) + /// } + /// + /// see + public class Pkcs10CertificationRequest + : CertificationRequest + { + private static readonly Hashtable algorithms = new Hashtable(); + private static readonly Hashtable exParams = new Hashtable(); + private static readonly Hashtable keyAlgorithms = new Hashtable(); + private static readonly Hashtable oids = new Hashtable(); + private static readonly ISet noParams = new HashSet(); + + static Pkcs10CertificationRequest() + { + algorithms.Add("MD2WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.2")); + algorithms.Add("MD2WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.2")); + algorithms.Add("MD5WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.4")); + algorithms.Add("MD5WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.4")); + algorithms.Add("RSAWITHMD5", new DerObjectIdentifier("1.2.840.113549.1.1.4")); + algorithms.Add("SHA1WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.5")); + algorithms.Add("SHA1WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.5")); + algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA1WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA224WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA256WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA384WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA512WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("RSAWITHSHA1", new DerObjectIdentifier("1.2.840.113549.1.1.5")); + algorithms.Add("RIPEMD160WITHRSAENCRYPTION", new DerObjectIdentifier("1.3.36.3.3.1.2")); + algorithms.Add("RIPEMD160WITHRSA", new DerObjectIdentifier("1.3.36.3.3.1.2")); + algorithms.Add("SHA1WITHDSA", new DerObjectIdentifier("1.2.840.10040.4.3")); + algorithms.Add("DSAWITHSHA1", new DerObjectIdentifier("1.2.840.10040.4.3")); + algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224); + algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256); + algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224); + algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256); + algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384); + algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512); + algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3410WITHGOST3411", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // reverse mappings + // + oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512WITHRSA"); + oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, "GOST3411WITHGOST3410"); + oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001, "GOST3411WITHECGOST3410"); + + oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA"); + oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA"); + oids.Add(new DerObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512WITHECDSA"); + oids.Add(OiwObjectIdentifiers.Sha1WithRsa, "SHA1WITHRSA"); + oids.Add(OiwObjectIdentifiers.DsaWithSha1, "SHA1WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha224, "SHA224WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha256, "SHA256WITHDSA"); + + // + // key types + // + keyAlgorithms.Add(PkcsObjectIdentifiers.RsaEncryption, "RSA"); + keyAlgorithms.Add(X9ObjectIdentifiers.IdDsa, "DSA"); + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add(NistObjectIdentifiers.DsaWithSha224); + noParams.Add(NistObjectIdentifiers.DsaWithSha256); + + // + // RFC 4491 + // + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // explicit params + // + AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + exParams.Add("SHA1WITHRSAANDMGF1", CreatePssParams(sha1AlgId, 20)); + + AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance); + exParams.Add("SHA224WITHRSAANDMGF1", CreatePssParams(sha224AlgId, 28)); + + AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance); + exParams.Add("SHA256WITHRSAANDMGF1", CreatePssParams(sha256AlgId, 32)); + + AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance); + exParams.Add("SHA384WITHRSAANDMGF1", CreatePssParams(sha384AlgId, 48)); + + AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance); + exParams.Add("SHA512WITHRSAANDMGF1", CreatePssParams(sha512AlgId, 64)); + } + + private static RsassaPssParameters CreatePssParams( + AlgorithmIdentifier hashAlgId, + int saltSize) + { + return new RsassaPssParameters( + hashAlgId, + new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId), + new DerInteger(saltSize), + new DerInteger(1)); + } + + public Pkcs10CertificationRequest( + byte[] encoded) + : base((Asn1Sequence) Asn1Object.FromByteArray(encoded)) + { + } + + public Pkcs10CertificationRequest( + Asn1Sequence seq) + : base(seq) + { + } + + public Pkcs10CertificationRequest( + Stream input) + : base((Asn1Sequence) Asn1Object.FromStream(input)) + { + } + + /// + /// Instantiate a Pkcs10CertificationRequest object with the necessary credentials. + /// + ///Name of Sig Alg. + /// X509Name of subject eg OU="My unit." O="My Organisatioin" C="au" + /// Public Key to be included in cert reqest. + /// ASN1Set of Attributes. + /// Matching Private key for nominated (above) public key to be used to sign the request. + public Pkcs10CertificationRequest( + string signatureAlgorithm, + X509Name subject, + AsymmetricKeyParameter publicKey, + Asn1Set attributes, + AsymmetricKeyParameter signingKey) + { + if (signatureAlgorithm == null) + throw new ArgumentNullException("signatureAlgorithm"); + if (subject == null) + throw new ArgumentNullException("subject"); + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + if (publicKey.IsPrivate) + throw new ArgumentException("expected public key", "publicKey"); + if (!signingKey.IsPrivate) + throw new ArgumentException("key for signing must be private", "signingKey"); + +// DerObjectIdentifier sigOid = SignerUtilities.GetObjectIdentifier(signatureAlgorithm); + string algorithmName = signatureAlgorithm.ToUpper(CultureInfo.InvariantCulture); + DerObjectIdentifier sigOid = (DerObjectIdentifier) algorithms[algorithmName]; + + if (sigOid == null) + throw new ArgumentException("Unknown signature type requested"); + + if (noParams.Contains(sigOid)) + { + this.sigAlgId = new AlgorithmIdentifier(sigOid); + } + else if (exParams.ContainsKey(algorithmName)) + { + this.sigAlgId = new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]); + } + else + { + this.sigAlgId = new AlgorithmIdentifier(sigOid, null); + } + + SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey); + + this.reqInfo = new CertificationRequestInfo(subject, pubInfo, attributes); + + ISigner sig = SignerUtilities.GetSigner(signatureAlgorithm); + + sig.Init(true, signingKey); + + try + { + // Encode. + byte[] b = reqInfo.GetDerEncoded(); + sig.BlockUpdate(b, 0, b.Length); + } + catch (Exception e) + { + throw new ArgumentException("exception encoding TBS cert request", e); + } + + // Generate Signature. + sigBits = new DerBitString(sig.GenerateSignature()); + } + +// internal Pkcs10CertificationRequest( +// Asn1InputStream seqStream) +// { +// Asn1Sequence seq = (Asn1Sequence) seqStream.ReadObject(); +// try +// { +// this.reqInfo = CertificationRequestInfo.GetInstance(seq[0]); +// this.sigAlgId = AlgorithmIdentifier.GetInstance(seq[1]); +// this.sigBits = (DerBitString) seq[2]; +// } +// catch (Exception ex) +// { +// throw new ArgumentException("Create From Asn1Sequence: " + ex.Message); +// } +// } + + /// + /// Get the public key. + /// + /// The public key. + public AsymmetricKeyParameter GetPublicKey() + { + return PublicKeyFactory.CreateKey(reqInfo.SubjectPublicKeyInfo); + } + + /// + /// Verify Pkcs10 Cert Request is valid. + /// + /// true = valid. + public bool Verify() + { + return Verify(this.GetPublicKey()); + } + + public bool Verify( + AsymmetricKeyParameter publicKey) + { + ISigner sig; + + try + { + sig = SignerUtilities.GetSigner(GetSignatureName(sigAlgId)); + } + catch (Exception e) + { + // try an alternate + string alt = (string) oids[sigAlgId.ObjectID]; + + if (alt != null) + { + sig = SignerUtilities.GetSigner(alt); + } + else + { + throw e; + } + } + + SetSignatureParameters(sig, sigAlgId.Parameters); + + sig.Init(false, publicKey); + + try + { + byte[] b = reqInfo.GetDerEncoded(); + sig.BlockUpdate(b, 0, b.Length); + } + catch (Exception e) + { + throw new SignatureException("exception encoding TBS cert request", e); + } + + return sig.VerifySignature(sigBits.GetBytes()); + } + +// /// +// /// Get the Der Encoded Pkcs10 Certification Request. +// /// +// /// A byte array. +// public byte[] GetEncoded() +// { +// return new CertificationRequest(reqInfo, sigAlgId, sigBits).GetDerEncoded(); +// } + + // TODO Figure out how to set parameters on an ISigner + private void SetSignatureParameters( + ISigner signature, + Asn1Encodable asn1Params) + { + if (asn1Params != null && !(asn1Params is Asn1Null)) + { +// AlgorithmParameters sigParams = AlgorithmParameters.GetInstance(signature.getAlgorithm()); +// +// try +// { +// sigParams.init(asn1Params.ToAsn1Object().GetDerEncoded()); +// } +// catch (IOException e) +// { +// throw new SignatureException("IOException decoding parameters: " + e.Message); +// } + + if (signature.AlgorithmName.EndsWith("MGF1")) + { + throw Platform.CreateNotImplementedException("signature algorithm with MGF1"); + +// try +// { +// signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class)); +// } +// catch (GeneralSecurityException e) +// { +// throw new SignatureException("Exception extracting parameters: " + e.getMessage()); +// } + } + } + } + + internal static string GetSignatureName( + AlgorithmIdentifier sigAlgId) + { + Asn1Encodable asn1Params = sigAlgId.Parameters; + + if (asn1Params != null && !(asn1Params is Asn1Null)) + { + if (sigAlgId.ObjectID.Equals(PkcsObjectIdentifiers.IdRsassaPss)) + { + RsassaPssParameters rsaParams = RsassaPssParameters.GetInstance(asn1Params); + return GetDigestAlgName(rsaParams.HashAlgorithm.ObjectID) + "withRSAandMGF1"; + } + } + + return sigAlgId.ObjectID.Id; + } + + private static string GetDigestAlgName( + DerObjectIdentifier digestAlgOID) + { + if (PkcsObjectIdentifiers.MD5.Equals(digestAlgOID)) + { + return "MD5"; + } + else if (OiwObjectIdentifiers.IdSha1.Equals(digestAlgOID)) + { + return "SHA1"; + } + else if (NistObjectIdentifiers.IdSha224.Equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NistObjectIdentifiers.IdSha256.Equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NistObjectIdentifiers.IdSha384.Equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NistObjectIdentifiers.IdSha512.Equals(digestAlgOID)) + { + return "SHA512"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD128.Equals(digestAlgOID)) + { + return "RIPEMD128"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD160.Equals(digestAlgOID)) + { + return "RIPEMD160"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD256.Equals(digestAlgOID)) + { + return "RIPEMD256"; + } + else if (CryptoProObjectIdentifiers.GostR3411.Equals(digestAlgOID)) + { + return "GOST3411"; + } + else + { + return digestAlgOID.Id; + } + } + } +} diff --git a/src/core/srcbc/pkcs/Pkcs12Entry.cs b/src/core/srcbc/pkcs/Pkcs12Entry.cs new file mode 100644 index 0000000..714e40d --- /dev/null +++ b/src/core/srcbc/pkcs/Pkcs12Entry.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Pkcs +{ + public abstract class Pkcs12Entry + { + private readonly Hashtable attributes; + + protected internal Pkcs12Entry( + Hashtable attributes) + { + this.attributes = attributes; + + foreach (DictionaryEntry entry in attributes) + { + if (!(entry.Key is string)) + throw new ArgumentException("Attribute keys must be of type: " + typeof(string).FullName, "attributes"); + if (!(entry.Value is Asn1Encodable)) + throw new ArgumentException("Attribute values must be of type: " + typeof(Asn1Encodable).FullName, "attributes"); + } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public Asn1Encodable GetBagAttribute( + DerObjectIdentifier oid) + { + return (Asn1Encodable)this.attributes[oid.Id]; + } + + [Obsolete("Use 'object[index]' syntax instead")] + public Asn1Encodable GetBagAttribute( + string oid) + { + return (Asn1Encodable)this.attributes[oid]; + } + + [Obsolete("Use 'BagAttributeKeys' property")] + public IEnumerator GetBagAttributeKeys() + { + return this.attributes.Keys.GetEnumerator(); + } + + public Asn1Encodable this[ + DerObjectIdentifier oid] + { + get { return (Asn1Encodable) this.attributes[oid.Id]; } + } + + public Asn1Encodable this[ + string oid] + { + get { return (Asn1Encodable) this.attributes[oid]; } + } + + public IEnumerable BagAttributeKeys + { + get { return new EnumerableProxy(this.attributes.Keys); } + } + } +} diff --git a/src/core/srcbc/pkcs/Pkcs12Store.cs b/src/core/srcbc/pkcs/Pkcs12Store.cs new file mode 100644 index 0000000..920683f --- /dev/null +++ b/src/core/srcbc/pkcs/Pkcs12Store.cs @@ -0,0 +1,1129 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkcs +{ + public class Pkcs12Store + { + private readonly IgnoresCaseHashtable keys = new IgnoresCaseHashtable(); + private readonly Hashtable localIds = new Hashtable(); + private readonly IgnoresCaseHashtable certs = new IgnoresCaseHashtable(); + private readonly Hashtable chainCerts = new Hashtable(); + private readonly Hashtable keyCerts = new Hashtable(); + + private static readonly DerObjectIdentifier keyAlgorithm = PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc; + private static readonly DerObjectIdentifier CertAlgorithm = PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc; + private int MinIterations = 1024; + private int saltSize = 20; + + private static SubjectKeyIdentifier CreateSubjectKeyID( + AsymmetricKeyParameter pubKey) + { + return new SubjectKeyIdentifier( + SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey)); + } + + internal class CertId + { + private readonly byte[] id; + + internal CertId( + AsymmetricKeyParameter pubKey) + { + this.id = CreateSubjectKeyID(pubKey).GetKeyIdentifier(); + } + + internal CertId( + byte[] id) + { + this.id = id; + } + + internal byte[] Id + { + get { return id; } + } + + public override int GetHashCode() + { + return Arrays.GetHashCode(id); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + CertId other = obj as CertId; + + if (other == null) + return false; + + return Arrays.AreEqual(id, other.id); + } + } + + public Pkcs12Store() + { + } + + public Pkcs12Store( + Stream input, + char[] password) + { + if (input == null) + throw new ArgumentNullException("input"); + if (password == null) + throw new ArgumentNullException("password"); + + Asn1Sequence obj = (Asn1Sequence) Asn1Object.FromStream(input); + Pfx bag = new Pfx(obj); + ContentInfo info = bag.AuthSafe; + bool unmarkedKey = false; + bool wrongPkcs12Zero = false; + + if (bag.MacData != null) // check the mac code + { + MacData mData = bag.MacData; + DigestInfo dInfo = mData.Mac; + AlgorithmIdentifier algId = dInfo.AlgorithmID; + byte[] salt = mData.GetSalt(); + int itCount = mData.IterationCount.IntValue; + + byte[] data = ((Asn1OctetString) info.Content).GetOctets(); + + byte[] mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, false, data); + byte[] dig = dInfo.GetDigest(); + + if (!Arrays.AreEqual(mac, dig)) + { + if (password.Length > 0) + throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file."); + + // Try with incorrect zero length password + mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, true, data); + + if (!Arrays.AreEqual(mac, dig)) + throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file."); + + wrongPkcs12Zero = true; + } + } + + ArrayList chain = new ArrayList(); + + if (info.ContentType.Equals(PkcsObjectIdentifiers.Data)) + { + byte[] octs = ((Asn1OctetString)info.Content).GetOctets(); + AuthenticatedSafe authSafe = new AuthenticatedSafe( + (Asn1Sequence) Asn1OctetString.FromByteArray(octs)); + ContentInfo[] cis = authSafe.GetContentInfo(); + + foreach (ContentInfo ci in cis) + { + DerObjectIdentifier oid = ci.ContentType; + + if (oid.Equals(PkcsObjectIdentifiers.Data)) + { + byte[] octets = ((Asn1OctetString)ci.Content).GetOctets(); + Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); + + foreach (Asn1Sequence subSeq in seq) + { + SafeBag b = new SafeBag(subSeq); + + if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag)) + { + EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue); + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo( + password, wrongPkcs12Zero, eIn); + AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo); + + // + // set the attributes on the key + // + Hashtable attributes = new Hashtable(); + AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); + string alias = null; + Asn1OctetString localId = null; + + if (b.BagAttributes != null) + { + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; + Asn1Set attrSet = (Asn1Set) sq[1]; + Asn1Encodable attr = null; + + if (attrSet.Count > 0) + { + // TODO We should be adding all attributes in the set + attr = attrSet[0]; + + // TODO We might want to "merge" attribute sets with + // the same OID - currently, it is an error + attributes.Add(aOid.Id, attr); + + if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + { + alias = ((DerBmpString)attr).GetString(); + // TODO Do these in a separate loop, just collect aliases here + keys[alias] = pkcs12Key; + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + } + + if (localId != null) + { + byte[] hex = Hex.Encode(localId.GetOctets()); + string name = Encoding.ASCII.GetString(hex, 0, hex.Length); + + if (alias == null) + { + keys[name] = pkcs12Key; + } + else + { + // TODO There may have been more than one alias + localIds[alias] = name; + } + } + else + { + unmarkedKey = true; + keys["unmarked"] = pkcs12Key; + } + } + else if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) + { + chain.Add(b); + } + else + { + Console.WriteLine("extra " + b.BagID); + Console.WriteLine("extra " + Asn1Dump.DumpAsString(b)); + } + } + } + else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData)) + { + EncryptedData d = EncryptedData.GetInstance(ci.Content); + byte[] octets = CryptPbeData(false, d.EncryptionAlgorithm, + password, wrongPkcs12Zero, d.Content.GetOctets()); + Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); + + foreach (Asn1Sequence subSeq in seq) + { + SafeBag b = new SafeBag(subSeq); + + if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) + { + chain.Add(b); + } + else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag)) + { + EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue); + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo( + password, wrongPkcs12Zero, eIn); + AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo); + + // + // set the attributes on the key + // + Hashtable attributes = new Hashtable(); + AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); + string alias = null; + Asn1OctetString localId = null; + + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; + Asn1Set attrSet = (Asn1Set) sq[1]; + Asn1Encodable attr = null; + + if (attrSet.Count > 0) + { + // TODO We should be adding all attributes in the set + attr = attrSet[0]; + + // TODO We might want to "merge" attribute sets with + // the same OID - currently, it is an error + attributes.Add(aOid.Id, attr); + + if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + { + alias = ((DerBmpString)attr).GetString(); + // TODO Do these in a separate loop, just collect aliases here + keys[alias] = pkcs12Key; + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + + // TODO Should we be checking localIds != null here + // as for PkcsObjectIdentifiers.Data version above? + + byte[] hex = Hex.Encode(localId.GetOctets()); + string name = Encoding.ASCII.GetString(hex, 0, hex.Length); + + if (alias == null) + { + keys[name] = pkcs12Key; + } + else + { + // TODO There may have been more than one alias + localIds[alias] = name; + } + } + else if (b.BagID.Equals(PkcsObjectIdentifiers.KeyBag)) + { + PrivateKeyInfo privKeyInfo = PrivateKeyInfo.GetInstance(b.BagValue); + AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo); + + // + // set the attributes on the key + // + string alias = null; + Asn1OctetString localId = null; + Hashtable attributes = new Hashtable(); + AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); + + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; + Asn1Set attrSet = (Asn1Set) sq[1]; + Asn1Encodable attr = null; + + if (attrSet.Count > 0) + { + // TODO We should be adding all attributes in the set + attr = attrSet[0]; + + // TODO We might want to "merge" attribute sets with + // the same OID - currently, it is an error + attributes.Add(aOid.Id, attr); + + if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + { + alias = ((DerBmpString)attr).GetString(); + // TODO Do these in a separate loop, just collect aliases here + keys[alias] = pkcs12Key; + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + + // TODO Should we be checking localIds != null here + // as for PkcsObjectIdentifiers.Data version above? + + byte[] hex = Hex.Encode(localId.GetOctets()); + string name = Encoding.ASCII.GetString(hex, 0, hex.Length); + + if (alias == null) + { + keys[name] = pkcs12Key; + } + else + { + // TODO There may have been more than one alias + localIds[alias] = name; + } + } + else + { + Console.WriteLine("extra " + b.BagID); + Console.WriteLine("extra " + Asn1Dump.DumpAsString(b)); + } + } + } + else + { + Console.WriteLine("extra " + oid); + Console.WriteLine("extra " + Asn1Dump.DumpAsString(ci.Content)); + } + } + } + + certs = new IgnoresCaseHashtable(); + chainCerts = new Hashtable(); + keyCerts = new Hashtable(); + + foreach (SafeBag b in chain) + { + CertBag cb = new CertBag((Asn1Sequence)b.BagValue); + byte[] octets = ((Asn1OctetString) cb.CertValue).GetOctets(); + X509Certificate cert = new X509CertificateParser().ReadCertificate(octets); + + // + // set the attributes + // + Hashtable attributes = new Hashtable(); + Asn1OctetString localId = null; + string alias = null; + + if (b.BagAttributes != null) + { + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; + Asn1Set attrSet = (Asn1Set) sq[1]; + + if (attrSet.Count > 0) + { + // TODO We should be adding all attributes in the set + Asn1Encodable attr = attrSet[0]; + + // TODO We might want to "merge" attribute sets with + // the same OID - currently, it is an error + attributes.Add(aOid.Id, attr); + + if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + { + alias = ((DerBmpString)attr).GetString(); + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + } + + CertId certId = new CertId(cert.GetPublicKey()); + X509CertificateEntry pkcs12Cert = new X509CertificateEntry(cert, attributes); + + chainCerts[certId] = pkcs12Cert; + + if (unmarkedKey) + { + if (keyCerts.Count == 0) + { + byte[] hex = Hex.Encode(certId.Id); + string name = Encoding.ASCII.GetString(hex, 0, hex.Length); + + keyCerts[name] = pkcs12Cert; + + object temp = keys["unmarked"]; + keys.Remove("unmarked"); + keys[name] = temp; + } + } + else + { + if (localId != null) + { + byte[] hex = Hex.Encode(localId.GetOctets()); + string name = Encoding.ASCII.GetString(hex, 0, hex.Length); + + keyCerts[name] = pkcs12Cert; + } + + if (alias != null) + { + // TODO There may have been more than one alias + certs[alias] = pkcs12Cert; + } + } + } + } + + public AsymmetricKeyEntry GetKey( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + return (AsymmetricKeyEntry)keys[alias]; + } + + public bool IsCertificateEntry( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + return (certs[alias] != null && keys[alias] == null); + } + + public bool IsKeyEntry( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + return (keys[alias] != null); + } + + private Hashtable GetAliasesTable() + { + Hashtable tab = new Hashtable(); + + foreach (string key in certs.Keys) + { + tab[key] = "cert"; + } + + foreach (string a in keys.Keys) + { + if (tab[a] == null) + { + tab[a] = "key"; + } + } + + return tab; + } + + public IEnumerable Aliases + { + get { return new EnumerableProxy(GetAliasesTable().Keys); } + } + + public bool ContainsAlias( + string alias) + { + return certs[alias] != null || keys[alias] != null; + } + + /** + * simply return the cert entry for the private key + */ + public X509CertificateEntry GetCertificate( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + X509CertificateEntry c = (X509CertificateEntry) certs[alias]; + + // + // look up the key table - and try the local key id + // + if (c == null) + { + string id = (string)localIds[alias]; + if (id != null) + { + c = (X509CertificateEntry)keyCerts[id]; + } + else + { + c = (X509CertificateEntry)keyCerts[alias]; + } + } + + return c; + } + + public string GetCertificateAlias( + X509Certificate cert) + { + if (cert == null) + throw new ArgumentNullException("cert"); + + foreach (DictionaryEntry entry in certs) + { + X509CertificateEntry entryValue = (X509CertificateEntry) entry.Value; + if (entryValue.Certificate.Equals(cert)) + { + return (string) entry.Key; + } + } + + foreach (DictionaryEntry entry in keyCerts) + { + X509CertificateEntry entryValue = (X509CertificateEntry) entry.Value; + if (entryValue.Certificate.Equals(cert)) + { + return (string) entry.Key; + } + } + + return null; + } + + public X509CertificateEntry[] GetCertificateChain( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + if (!IsKeyEntry(alias)) + { + return null; + } + + X509CertificateEntry c = GetCertificate(alias); + + if (c != null) + { + ArrayList cs = new ArrayList(); + + while (c != null) + { + X509Certificate x509c = c.Certificate; + X509CertificateEntry nextC = null; + + Asn1OctetString ext = x509c.GetExtensionValue(X509Extensions.AuthorityKeyIdentifier); + if (ext != null) + { + AuthorityKeyIdentifier id = AuthorityKeyIdentifier.GetInstance( + Asn1Object.FromByteArray(ext.GetOctets())); + + if (id.GetKeyIdentifier() != null) + { + nextC = (X509CertificateEntry) chainCerts[new CertId(id.GetKeyIdentifier())]; + } + } + + if (nextC == null) + { + // + // no authority key id, try the Issuer DN + // + X509Name i = x509c.IssuerDN; + X509Name s = x509c.SubjectDN; + + if (!i.Equivalent(s)) + { + foreach (CertId certId in chainCerts.Keys) + { + X509CertificateEntry x509CertEntry = (X509CertificateEntry) chainCerts[certId]; + + X509Certificate crt = x509CertEntry.Certificate; + + X509Name sub = crt.SubjectDN; + if (sub.Equivalent(i)) + { + try + { + x509c.Verify(crt.GetPublicKey()); + + nextC = x509CertEntry; + break; + } + catch (InvalidKeyException) + { + // TODO What if it doesn't verify? + } + } + } + } + } + + cs.Add(c); + if (nextC != c) // self signed - end of the chain + { + c = nextC; + } + else + { + c = null; + } + } + + return (X509CertificateEntry[]) cs.ToArray(typeof(X509CertificateEntry)); + } + + return null; + } + + public void SetCertificateEntry( + string alias, + X509CertificateEntry certEntry) + { + if (alias == null) + throw new ArgumentNullException("alias"); + if (certEntry == null) + throw new ArgumentNullException("certEntry"); + if (keys[alias] != null) + throw new ArgumentException("There is a key entry with the name " + alias + "."); + + certs[alias] = certEntry; + chainCerts[new CertId(certEntry.Certificate.GetPublicKey())] = certEntry; + } + + public void SetKeyEntry( + string alias, + AsymmetricKeyEntry keyEntry, + X509CertificateEntry[] chain) + { + if (alias == null) + throw new ArgumentNullException("alias"); + if (keyEntry == null) + throw new ArgumentNullException("keyEntry"); + if (keyEntry.Key.IsPrivate && (chain == null)) + throw new ArgumentException("No certificate chain for private key"); + + if (keys[alias] != null) + { + DeleteEntry(alias); + } + + keys[alias] = keyEntry; + certs[alias] = chain[0]; + + for (int i = 0; i != chain.Length; i++) + { + chainCerts[new CertId(chain[i].Certificate.GetPublicKey())] = chain[i]; + } + } + + public void DeleteEntry( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + AsymmetricKeyEntry k = (AsymmetricKeyEntry)keys[alias]; + if (k != null) + { + keys.Remove(alias); + } + + X509CertificateEntry c = (X509CertificateEntry)certs[alias]; + + if (c != null) + { + certs.Remove(alias); + chainCerts.Remove(new CertId(c.Certificate.GetPublicKey())); + } + + if (k != null) + { + string id = (string)localIds[alias]; + if (id != null) + { + localIds.Remove(alias); + c = (X509CertificateEntry)keyCerts[id]; + } + if (c != null) + { + keyCerts.Remove(id); + chainCerts.Remove(new CertId(c.Certificate.GetPublicKey())); + } + } + + if (c == null && k == null) + { + throw new ArgumentException("no such entry as " + alias); + } + } + + public bool IsEntryOfType( + string alias, + Type entryType) + { + if (entryType == typeof(X509CertificateEntry)) + return IsCertificateEntry(alias); + + if (entryType == typeof(AsymmetricKeyEntry)) + return IsKeyEntry(alias) && GetCertificate(alias) != null; + + return false; + } + + [Obsolete("Use 'Count' property instead")] + public int Size() + { + return Count; + } + + public int Count + { + // TODO Seems a little inefficient + get { return GetAliasesTable().Count; } + } + + public void Save( + Stream stream, + char[] password, + SecureRandom random) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (password == null) + throw new ArgumentNullException("password"); + if (random == null) + throw new ArgumentNullException("random"); + + // + // handle the key + // + Asn1EncodableVector keyS = new Asn1EncodableVector(); + foreach (string name in keys.Keys) + { + byte[] kSalt = new byte[saltSize]; + random.NextBytes(kSalt); + + AsymmetricKeyEntry privKey = (AsymmetricKeyEntry) keys[name]; + EncryptedPrivateKeyInfo kInfo = + EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( + keyAlgorithm, password, kSalt, MinIterations, privKey.Key); + + Asn1EncodableVector kName = new Asn1EncodableVector(); + + foreach (string oid in privKey.BagAttributeKeys) + { + Asn1Encodable entry = privKey[oid]; + + // NB: Ignore any existing FriendlyName + if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) + continue; + + kName.Add( + new DerSequence( + new DerObjectIdentifier(oid), + new DerSet(entry))); + } + + // + // make sure we are using the local alias on store + // + // NB: We always set the FriendlyName based on 'name' + //if (privKey[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) + { + kName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtFriendlyName, + new DerSet(new DerBmpString(name)))); + } + + // + // make sure we have a local key-id + // + if (privKey[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null) + { + X509CertificateEntry ct = GetCertificate(name); + AsymmetricKeyParameter pubKey = ct.Certificate.GetPublicKey(); + SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey); + + kName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtLocalKeyID, + new DerSet(subjectKeyID))); + } + + SafeBag kBag = new SafeBag(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag, kInfo.ToAsn1Object(), new DerSet(kName)); + keyS.Add(kBag); + } + + byte[] derEncodedBytes = new DerSequence(keyS).GetDerEncoded(); + + BerOctetString keyString = new BerOctetString(derEncodedBytes); + + // + // certificate processing + // + byte[] cSalt = new byte[saltSize]; + + random.NextBytes(cSalt); + + Asn1EncodableVector certSeq = new Asn1EncodableVector(); + Pkcs12PbeParams cParams = new Pkcs12PbeParams(cSalt, MinIterations); + AlgorithmIdentifier cAlgId = new AlgorithmIdentifier(CertAlgorithm, cParams.ToAsn1Object()); + ISet doneCerts = new HashSet(); + + foreach (string name in keys.Keys) + { + X509CertificateEntry certEntry = GetCertificate(name); + CertBag cBag = new CertBag( + PkcsObjectIdentifiers.X509Certificate, + new DerOctetString(certEntry.Certificate.GetEncoded())); + + Asn1EncodableVector fName = new Asn1EncodableVector(); + + foreach (string oid in certEntry.BagAttributeKeys) + { + Asn1Encodable entry = certEntry[oid]; + + // NB: Ignore any existing FriendlyName + if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) + continue; + + fName.Add( + new DerSequence( + new DerObjectIdentifier(oid), + new DerSet(entry))); + } + + // + // make sure we are using the local alias on store + // + // NB: We always set the FriendlyName based on 'name' + //if (certEntry[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) + { + fName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtFriendlyName, + new DerSet(new DerBmpString(name)))); + } + + // + // make sure we have a local key-id + // + if (certEntry[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null) + { + AsymmetricKeyParameter pubKey = certEntry.Certificate.GetPublicKey(); + SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey); + + fName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtLocalKeyID, + new DerSet(subjectKeyID))); + } + + SafeBag sBag = new SafeBag( + PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); + + certSeq.Add(sBag); + + doneCerts.Add(certEntry.Certificate); + } + + foreach (string certId in certs.Keys) + { + X509CertificateEntry cert = (X509CertificateEntry)certs[certId]; + + if (keys[certId] != null) + continue; + + CertBag cBag = new CertBag( + PkcsObjectIdentifiers.X509Certificate, + new DerOctetString(cert.Certificate.GetEncoded())); + + Asn1EncodableVector fName = new Asn1EncodableVector(); + + foreach (string oid in cert.BagAttributeKeys) + { + Asn1Encodable entry = cert[oid]; + + // NB: Ignore any existing FriendlyName + if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) + continue; + + fName.Add( + new DerSequence( + new DerObjectIdentifier(oid), + new DerSet(entry))); + } + + // + // make sure we are using the local alias on store + // + // NB: We always set the FriendlyName based on 'certId' + //if (cert[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) + { + fName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtFriendlyName, + new DerSet(new DerBmpString(certId)))); + } + + SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, + cBag.ToAsn1Object(), new DerSet(fName)); + + certSeq.Add(sBag); + + doneCerts.Add(cert.Certificate); + } + + foreach (CertId certId in chainCerts.Keys) + { + X509CertificateEntry cert = (X509CertificateEntry)chainCerts[certId]; + + if (doneCerts.Contains(cert.Certificate)) + continue; + + CertBag cBag = new CertBag( + PkcsObjectIdentifiers.X509Certificate, + new DerOctetString(cert.Certificate.GetEncoded())); + + Asn1EncodableVector fName = new Asn1EncodableVector(); + + foreach (string oid in cert.BagAttributeKeys) + { + fName.Add( + new DerSequence( + new DerObjectIdentifier(oid), + new DerSet(cert[oid]))); + } + + SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); + + certSeq.Add(sBag); + } + + derEncodedBytes = new DerSequence(certSeq).GetDerEncoded(); + + byte[] certBytes = CryptPbeData(true, cAlgId, password, false, derEncodedBytes); + + EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes)); + + ContentInfo[] info = new ContentInfo[] + { + new ContentInfo(PkcsObjectIdentifiers.Data, keyString), + new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object()) + }; + + byte[] data = new AuthenticatedSafe(info).GetEncoded(); + + ContentInfo mainInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(data)); + + // + // create the mac + // + byte[] mSalt = new byte[20]; + random.NextBytes(mSalt); + + byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1, + mSalt, MinIterations, password, false, data); + + AlgorithmIdentifier algId = new AlgorithmIdentifier( + OiwObjectIdentifiers.IdSha1, DerNull.Instance); + DigestInfo dInfo = new DigestInfo(algId, mac); + + MacData mData = new MacData(dInfo, mSalt, MinIterations); + + // + // output the Pfx + // + Pfx pfx = new Pfx(mainInfo, mData); + + BerOutputStream berOut = new BerOutputStream(stream); + berOut.WriteObject(pfx); + } + + private static byte[] CalculatePbeMac( + DerObjectIdentifier oid, + byte[] salt, + int itCount, + char[] password, + bool wrongPkcs12Zero, + byte[] data) + { + Asn1Encodable asn1Params = PbeUtilities.GenerateAlgorithmParameters( + oid, salt, itCount); + ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters( + oid, password, wrongPkcs12Zero, asn1Params); + + IMac mac = (IMac) PbeUtilities.CreateEngine(oid); + mac.Init(cipherParams); + mac.BlockUpdate(data, 0, data.Length); + return MacUtilities.DoFinal(mac); + } + + private static byte[] CryptPbeData( + bool forEncryption, + AlgorithmIdentifier algId, + char[] password, + bool wrongPkcs12Zero, + byte[] data) + { + Pkcs12PbeParams pbeParams = Pkcs12PbeParams.GetInstance(algId.Parameters); + ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters( + algId.ObjectID, password, wrongPkcs12Zero, pbeParams); + + IBufferedCipher cipher = PbeUtilities.CreateEngine(algId.ObjectID) as IBufferedCipher; + + if (cipher == null) + throw new Exception("Unknown encryption algorithm: " + algId.ObjectID); + + cipher.Init(forEncryption, cipherParams); + + return cipher.DoFinal(data); + } + + private class IgnoresCaseHashtable + : IEnumerable + { + private readonly Hashtable orig = new Hashtable(); + private readonly Hashtable keys = new Hashtable(); + + public IEnumerator GetEnumerator() + { + return orig.GetEnumerator(); + } + + public ICollection Keys + { + get { return orig.Keys; } + } + + public object Remove( + string alias) + { + string lower = alias.ToLower(CultureInfo.InvariantCulture); + string k = (string) keys[lower]; + + if (k == null) + return null; + + keys.Remove(lower); + + object o = orig[k]; + orig.Remove(k); + return o; + } + + public object this[ + string alias] + { + get + { + string lower = alias.ToLower(CultureInfo.InvariantCulture); + string k = (string) keys[lower]; + + if (k == null) + return null; + + return orig[k]; + } + set + { + string lower = alias.ToLower(CultureInfo.InvariantCulture); + string k = (string) keys[lower]; + if (k != null) + { + orig.Remove(k); + } + keys[lower] = alias; + orig[alias] = value; + } + } + + public ICollection Values + { + get { return orig.Values; } + } + } + } +} diff --git a/src/core/srcbc/pkcs/PrivateKeyInfoFactory.cs b/src/core/srcbc/pkcs/PrivateKeyInfoFactory.cs new file mode 100644 index 0000000..0964ef2 --- /dev/null +++ b/src/core/srcbc/pkcs/PrivateKeyInfoFactory.cs @@ -0,0 +1,211 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Pkcs +{ + public sealed class PrivateKeyInfoFactory + { + private PrivateKeyInfoFactory() + { + } + + public static PrivateKeyInfo CreatePrivateKeyInfo( + AsymmetricKeyParameter key) + { + if (key == null) + throw new ArgumentNullException("key"); + if (!key.IsPrivate) + throw new ArgumentException("Public key passed - private key expected", "key"); + + if (key is ElGamalPrivateKeyParameters) + { + ElGamalPrivateKeyParameters _key = (ElGamalPrivateKeyParameters)key; + return new PrivateKeyInfo( + new AlgorithmIdentifier( + OiwObjectIdentifiers.ElGamalAlgorithm, + new ElGamalParameter( + _key.Parameters.P, + _key.Parameters.G).ToAsn1Object()), + new DerInteger(_key.X)); + } + + if (key is DsaPrivateKeyParameters) + { + DsaPrivateKeyParameters _key = (DsaPrivateKeyParameters)key; + return new PrivateKeyInfo( + new AlgorithmIdentifier( + X9ObjectIdentifiers.IdDsa, + new DsaParameter( + _key.Parameters.P, + _key.Parameters.Q, + _key.Parameters.G).ToAsn1Object()), + new DerInteger(_key.X)); + } + + if (key is DHPrivateKeyParameters) + { + /* + Process DH private key. + The value for L was set to zero implicitly. + This is the same action as found in JCEDHPrivateKey GetEncoded method. + */ + + DHPrivateKeyParameters _key = (DHPrivateKeyParameters)key; + + DHParameter withNewL = new DHParameter( + _key.Parameters.P, _key.Parameters.G, 0); + + return new PrivateKeyInfo( + new AlgorithmIdentifier( + PkcsObjectIdentifiers.DhKeyAgreement, + withNewL.ToAsn1Object()), + new DerInteger(_key.X)); + } + + if (key is RsaKeyParameters) + { + AlgorithmIdentifier algID = new AlgorithmIdentifier( + PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance); + + RsaPrivateKeyStructure keyStruct; + if (key is RsaPrivateCrtKeyParameters) + { + RsaPrivateCrtKeyParameters _key = (RsaPrivateCrtKeyParameters)key; + + keyStruct = new RsaPrivateKeyStructure( + _key.Modulus, + _key.PublicExponent, + _key.Exponent, + _key.P, + _key.Q, + _key.DP, + _key.DQ, + _key.QInv); + } + else + { + RsaKeyParameters _key = (RsaKeyParameters) key; + + keyStruct = new RsaPrivateKeyStructure( + _key.Modulus, + BigInteger.Zero, + _key.Exponent, + BigInteger.Zero, + BigInteger.Zero, + BigInteger.Zero, + BigInteger.Zero, + BigInteger.Zero); + } + + return new PrivateKeyInfo(algID, keyStruct.ToAsn1Object()); + } + + if (key is ECPrivateKeyParameters) + { + ECPrivateKeyParameters _key = (ECPrivateKeyParameters)key; + AlgorithmIdentifier algID; + + if (_key.AlgorithmName == "ECGOST3410") + { + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + _key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet); + + algID = new AlgorithmIdentifier( + CryptoProObjectIdentifiers.GostR3410x2001, + gostParams.ToAsn1Object()); + } + else + { + X9ECParameters ecP = new X9ECParameters( + _key.Parameters.Curve, + _key.Parameters.G, + _key.Parameters.N, + _key.Parameters.H, + _key.Parameters.GetSeed()); + + X962Parameters x962 = new X962Parameters(ecP); + + algID = new AlgorithmIdentifier( + X9ObjectIdentifiers.IdECPublicKey, + x962.ToAsn1Object()); + } + + return new PrivateKeyInfo(algID, new ECPrivateKeyStructure(_key.D).ToAsn1Object()); + } + + if (key is Gost3410PrivateKeyParameters) + { + Gost3410PrivateKeyParameters _key = (Gost3410PrivateKeyParameters)key; + + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + byte[] keyEnc = _key.X.ToByteArrayUnsigned(); + byte[] keyBytes = new byte[keyEnc.Length]; + + for (int i = 0; i != keyBytes.Length; i++) + { + keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // must be little endian + } + + Gost3410PublicKeyAlgParameters algParams = new Gost3410PublicKeyAlgParameters( + _key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet, null); + + AlgorithmIdentifier algID = new AlgorithmIdentifier( + CryptoProObjectIdentifiers.GostR3410x94, + algParams.ToAsn1Object()); + + return new PrivateKeyInfo(algID, new DerOctetString(keyBytes)); + } + + throw new ArgumentException("Class provided is not convertible: " + key.GetType().FullName); + } + + public static PrivateKeyInfo CreatePrivateKeyInfo( + char[] passPhrase, + EncryptedPrivateKeyInfo encInfo) + { + return CreatePrivateKeyInfo(passPhrase, false, encInfo); + } + + public static PrivateKeyInfo CreatePrivateKeyInfo( + char[] passPhrase, + bool wrongPkcs12Zero, + EncryptedPrivateKeyInfo encInfo) + { + AlgorithmIdentifier algID = encInfo.EncryptionAlgorithm; + IBufferedCipher cipher = PbeUtilities.CreateEngine(algID.ObjectID) as IBufferedCipher; + + if (cipher == null) + { + // TODO Throw exception? + } + + ICipherParameters keyParameters = PbeUtilities.GenerateCipherParameters( + algID.ObjectID, passPhrase, wrongPkcs12Zero, algID.Parameters); + + cipher.Init(false, keyParameters); + + byte[] keyBytes = encInfo.GetEncryptedData(); + byte[] encoding = cipher.DoFinal(keyBytes); + Asn1Object asn1Data = Asn1Object.FromByteArray(encoding); + + return PrivateKeyInfo.GetInstance(asn1Data); + } + } +} diff --git a/src/core/srcbc/pkcs/X509CertificateEntry.cs b/src/core/srcbc/pkcs/X509CertificateEntry.cs new file mode 100644 index 0000000..8566425 --- /dev/null +++ b/src/core/srcbc/pkcs/X509CertificateEntry.cs @@ -0,0 +1,32 @@ +using System.Collections; + +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkcs +{ + public class X509CertificateEntry + : Pkcs12Entry + { + private readonly X509Certificate cert; + + public X509CertificateEntry( + X509Certificate cert) + : base(new Hashtable()) + { + this.cert = cert; + } + + public X509CertificateEntry( + X509Certificate cert, + Hashtable attributes) + : base(attributes) + { + this.cert = cert; + } + + public X509Certificate Certificate + { + get { return this.cert; } + } + } +} diff --git a/src/core/srcbc/security/AgreementUtilities.cs b/src/core/srcbc/security/AgreementUtilities.cs new file mode 100644 index 0000000..704dbcc --- /dev/null +++ b/src/core/srcbc/security/AgreementUtilities.cs @@ -0,0 +1,99 @@ +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Agreement.Kdf; +using Org.BouncyCastle.Crypto.Digests; + +namespace Org.BouncyCastle.Security +{ + /// + /// Utility class for creating IBasicAgreement objects from their names/Oids + /// + public sealed class AgreementUtilities + { + private AgreementUtilities() + { + } + + private static readonly Hashtable algorithms = new Hashtable(); + // private static readonly Hashtable oids = new Hashtable(); + + static AgreementUtilities() + { + //algorithms[X9ObjectIdentifiers.DHSinglePassCofactorDHSha1KdfScheme.Id] = ?; + algorithms[X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme.Id] = "DHWITHSHA1KDF"; + //algorithms[X9ObjectIdentifiers.MqvSinglePassSha1KdfScheme.Id] = ?; + } + + public static IBasicAgreement GetBasicAgreement( + DerObjectIdentifier oid) + { + return GetBasicAgreement(oid.Id); + } + + public static IBasicAgreement GetBasicAgreement( + string algorithm) + { + string upper = algorithm.ToUpper(CultureInfo.InvariantCulture); + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + switch (mechanism) + { + case "DH": + return new DHBasicAgreement(); + case "ECDH": + return new ECDHBasicAgreement(); + case "ECDHC": + return new ECDHCBasicAgreement(); + } + + throw new SecurityUtilityException("Basic Agreement " + algorithm + " not recognised."); + } + + public static IBasicAgreement GetBasicAgreementWithKdf( + DerObjectIdentifier oid, + string wrapAlgorithm) + { + return GetBasicAgreementWithKdf(oid.Id, wrapAlgorithm); + } + + public static IBasicAgreement GetBasicAgreementWithKdf( + string agreeAlgorithm, + string wrapAlgorithm) + { + string upper = agreeAlgorithm.ToUpper(CultureInfo.InvariantCulture); + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + switch (mechanism) + { + case "DHWITHSHA1KDF": + return new ECDHWithKdfBasicAgreement( + wrapAlgorithm, + new ECDHKekGenerator( + new Sha1Digest())); + } + + throw new SecurityUtilityException("Basic Agreement (with KDF) " + agreeAlgorithm + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + } +} diff --git a/src/core/srcbc/security/CipherUtilities.cs b/src/core/srcbc/security/CipherUtilities.cs new file mode 100644 index 0000000..4c3372e --- /dev/null +++ b/src/core/srcbc/security/CipherUtilities.cs @@ -0,0 +1,566 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Security +{ + /// + /// Cipher Utility class contains methods that can not be specifically grouped into other classes. + /// + public sealed class CipherUtilities + { + private static readonly Hashtable algorithms = new Hashtable(); + private static readonly Hashtable oids = new Hashtable(); + + static CipherUtilities() + { + // TODO Flesh out the list of aliases + + algorithms[NistObjectIdentifiers.IdAes128Ecb.Id] = "AES/ECB/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes192Ecb.Id] = "AES/ECB/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes256Ecb.Id] = "AES/ECB/PKCS7PADDING"; + algorithms["AES/ECB/PKCS7"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES//PKCS7"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES//PKCS7PADDING"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES/ECB/PKCS5"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES/ECB/PKCS5PADDING"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES//PKCS5"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES//PKCS5PADDING"] = "AES/ECB/PKCS7PADDING"; + + algorithms[NistObjectIdentifiers.IdAes128Cbc.Id] = "AES/CBC/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes192Cbc.Id] = "AES/CBC/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes256Cbc.Id] = "AES/CBC/PKCS7PADDING"; + algorithms["AES/CBC/PKCS7"] = "AES/CBC/PKCS7PADDING"; + algorithms["AES/CBC/PKCS5"] = "AES/CBC/PKCS7PADDING"; + algorithms["AES/CBC/PKCS5PADDING"] = "AES/CBC/PKCS7PADDING"; + + algorithms[NistObjectIdentifiers.IdAes128Ofb.Id] = "AES/OFB/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes192Ofb.Id] = "AES/OFB/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes256Ofb.Id] = "AES/OFB/PKCS7PADDING"; + algorithms["AES/OFB/PKCS7"] = "AES/OFB/PKCS7PADDING"; + algorithms["AES/OFB/PKCS5"] = "AES/OFB/PKCS7PADDING"; + algorithms["AES/OFB/PKCS5PADDING"] = "AES/OFB/PKCS7PADDING"; + + algorithms[NistObjectIdentifiers.IdAes128Cfb.Id] = "AES/CFB/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes192Cfb.Id] = "AES/CFB/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes256Cfb.Id] = "AES/CFB/PKCS7PADDING"; + algorithms["AES/CFB/PKCS7"] = "AES/CFB/PKCS7PADDING"; + algorithms["AES/CFB/PKCS5"] = "AES/CFB/PKCS7PADDING"; + algorithms["AES/CFB/PKCS5PADDING"] = "AES/CFB/PKCS7PADDING"; + + algorithms["RSA//PKCS1"] = "RSA//PKCS1PADDING"; + algorithms["RSA/ECB/PKCS1"] = "RSA//PKCS1PADDING"; + algorithms["RSA/ECB/PKCS1PADDING"] = "RSA//PKCS1PADDING"; + algorithms[PkcsObjectIdentifiers.RsaEncryption.Id] = "RSA//PKCS1PADDING"; + + algorithms[OiwObjectIdentifiers.DesCbc.Id] = "DES/CBC"; + algorithms[PkcsObjectIdentifiers.DesEde3Cbc.Id] = "DESEDE/CBC"; + algorithms[PkcsObjectIdentifiers.RC2Cbc.Id] = "RC2/CBC"; + algorithms["1.3.6.1.4.1.188.7.1.1.2"] = "IDEA/CBC"; + algorithms["1.2.840.113533.7.66.10"] = "CAST5/CBC"; + + algorithms["RC4"] = "ARC4"; + algorithms["ARCFOUR"] = "ARC4"; + algorithms["1.2.840.113549.3.4"] = "ARC4"; + + + + algorithms["PBEWITHSHA1AND128BITRC4"] = "PBEWITHSHAAND128BITRC4"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd128BitRC4.Id] = "PBEWITHSHAAND128BITRC4"; + algorithms["PBEWITHSHA1AND40BITRC4"] = "PBEWITHSHAAND40BITRC4"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd40BitRC4.Id] = "PBEWITHSHAAND40BITRC4"; + + algorithms["PBEWITHSHA1ANDDES"] = "PBEWITHSHA1ANDDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithSha1AndDesCbc.Id] = "PBEWITHSHA1ANDDES-CBC"; + algorithms["PBEWITHSHA1ANDRC2"] = "PBEWITHSHA1ANDRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithSha1AndRC2Cbc.Id] = "PBEWITHSHA1ANDRC2-CBC"; + + algorithms["PBEWITHSHA1AND3-KEYTRIPLEDES-CBC"] = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"; + algorithms["PBEWITHSHAAND3KEYTRIPLEDES"] = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc.Id] = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"; + algorithms["PBEWITHSHA1ANDDESEDE"] = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"; + + algorithms["PBEWITHSHA1AND2-KEYTRIPLEDES-CBC"] = "PBEWITHSHAAND2-KEYTRIPLEDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd2KeyTripleDesCbc.Id] = "PBEWITHSHAAND2-KEYTRIPLEDES-CBC"; + + algorithms["PBEWITHSHA1AND128BITRC2-CBC"] = "PBEWITHSHAAND128BITRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd128BitRC2Cbc.Id] = "PBEWITHSHAAND128BITRC2-CBC"; + + algorithms["PBEWITHSHA1AND40BITRC2-CBC"] = "PBEWITHSHAAND40BITRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc.Id] = "PBEWITHSHAAND40BITRC2-CBC"; + + algorithms["PBEWITHSHA1AND128BITAES-CBC-BC"] = "PBEWITHSHAAND128BITAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND128BITAES-CBC-BC"] = "PBEWITHSHAAND128BITAES-CBC-BC"; + + algorithms["PBEWITHSHA1AND192BITAES-CBC-BC"] = "PBEWITHSHAAND192BITAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND192BITAES-CBC-BC"] = "PBEWITHSHAAND192BITAES-CBC-BC"; + + algorithms["PBEWITHSHA1AND256BITAES-CBC-BC"] = "PBEWITHSHAAND256BITAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND256BITAES-CBC-BC"] = "PBEWITHSHAAND256BITAES-CBC-BC"; + + algorithms["PBEWITHSHA-256AND128BITAES-CBC-BC"] = "PBEWITHSHA256AND128BITAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND192BITAES-CBC-BC"] = "PBEWITHSHA256AND192BITAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND256BITAES-CBC-BC"] = "PBEWITHSHA256AND256BITAES-CBC-BC"; + + + algorithms["GOST"] = "GOST28147"; + algorithms["GOST-28147"] = "GOST28147"; + algorithms[CryptoProObjectIdentifiers.GostR28147Cbc.Id] = "GOST28147/CBC/PKCS7PADDING"; + + algorithms["RC5-32"] = "RC5"; + + algorithms[NttObjectIdentifiers.IdCamellia128Cbc.Id] = "CAMELLIA/CBC/PKCS7PADDING"; + algorithms[NttObjectIdentifiers.IdCamellia192Cbc.Id] = "CAMELLIA/CBC/PKCS7PADDING"; + algorithms[NttObjectIdentifiers.IdCamellia256Cbc.Id] = "CAMELLIA/CBC/PKCS7PADDING"; + + algorithms[KisaObjectIdentifiers.IdSeedCbc.Id] = "SEED/CBC/PKCS7PADDING"; + } + + private CipherUtilities() + { + } + + /// + /// Returns a ObjectIdentifier for a give encoding. + /// + /// A string representation of the encoding. + /// A DerObjectIdentifier, null if the Oid is not available. + // TODO Don't really want to support this + public static DerObjectIdentifier GetObjectIdentifier( + string mechanism) + { + if (mechanism == null) + throw new ArgumentNullException("mechanism"); + + mechanism = mechanism.ToUpper(CultureInfo.InvariantCulture); + string aliased = (string) algorithms[mechanism]; + + if (aliased != null) + mechanism = aliased; + + return (DerObjectIdentifier) oids[mechanism]; + } + + public static ICollection Algorithms + { + get { return oids.Keys; } + } + + public static IBufferedCipher GetCipher( + DerObjectIdentifier oid) + { + return GetCipher(oid.Id); + } + + public static IBufferedCipher GetCipher( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + algorithm = algorithm.ToUpper(CultureInfo.InvariantCulture); + + string aliased = (string) algorithms[algorithm]; + + if (aliased != null) + algorithm = aliased; + + + + IBasicAgreement iesAgreement = null; + if (algorithm == "IES") + { + iesAgreement = new DHBasicAgreement(); + } + else if (algorithm == "ECIES") + { + iesAgreement = new ECDHBasicAgreement(); + } + + if (iesAgreement != null) + { + return new BufferedIesCipher( + new IesEngine( + iesAgreement, + new Kdf2BytesGenerator( + new Sha1Digest()), + new HMac( + new Sha1Digest()))); + } + + + + if (algorithm.StartsWith("PBE")) + { + switch (algorithm) + { + case "PBEWITHSHAAND2-KEYTRIPLEDES-CBC": + case "PBEWITHSHAAND3-KEYTRIPLEDES-CBC": + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new DesEdeEngine())); + + case "PBEWITHSHAAND128BITRC2-CBC": + case "PBEWITHSHAAND40BITRC2-CBC": + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new RC2Engine())); + + case "PBEWITHSHAAND128BITAES-CBC-BC": + case "PBEWITHSHAAND192BITAES-CBC-BC": + case "PBEWITHSHAAND256BITAES-CBC-BC": + case "PBEWITHSHA256AND128BITAES-CBC-BC": + case "PBEWITHSHA256AND192BITAES-CBC-BC": + case "PBEWITHSHA256AND256BITAES-CBC-BC": + case "PBEWITHMD5AND128BITAES-CBC-OPENSSL": + case "PBEWITHMD5AND192BITAES-CBC-OPENSSL": + case "PBEWITHMD5AND256BITAES-CBC-OPENSSL": + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new AesFastEngine())); + + case "PBEWITHSHA1ANDDES-CBC": + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new DesEngine())); + + case "PBEWITHSHA1ANDRC2-CBC": + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new RC2Engine())); + } + } + + + + string[] parts = algorithm.Split('/'); + + IBlockCipher blockCipher = null; + IAsymmetricBlockCipher asymBlockCipher = null; + IStreamCipher streamCipher = null; + + switch (parts[0]) + { + case "AES": + blockCipher = new AesFastEngine(); + break; + case "ARC4": + streamCipher = new RC4Engine(); + break; + case "BLOWFISH": + blockCipher = new BlowfishEngine(); + break; + case "CAMELLIA": + blockCipher = new CamelliaEngine(); + break; + case "CAST5": + blockCipher = new Cast5Engine(); + break; + case "CAST6": + blockCipher = new Cast6Engine(); + break; + case "DES": + blockCipher = new DesEngine(); + break; + case "DESEDE": + blockCipher = new DesEdeEngine(); + break; + case "ELGAMAL": + asymBlockCipher = new ElGamalEngine(); + break; + case "GOST28147": + blockCipher = new Gost28147Engine(); + break; + case "HC128": + streamCipher = new HC128Engine(); + break; + case "HC256": + streamCipher = new HC256Engine(); + break; + case "IDEA": + blockCipher = new IdeaEngine(); + break; + case "NOEKEON": + blockCipher = new NoekeonEngine(); + break; + case "PBEWITHSHAAND128BITRC4": + case "PBEWITHSHAAND40BITRC4": + streamCipher = new RC4Engine(); + break; + case "RC2": + blockCipher = new RC2Engine(); + break; + case "RC5": + blockCipher = new RC532Engine(); + break; + case "RC5-64": + blockCipher = new RC564Engine(); + break; + case "RC6": + blockCipher = new RC6Engine(); + break; + case "RIJNDAEL": + blockCipher = new RijndaelEngine(); + break; + case "RSA": + asymBlockCipher = new RsaBlindedEngine(); + break; + case "SALSA20": + streamCipher = new Salsa20Engine(); + break; + case "SEED": + blockCipher = new SeedEngine(); + break; + case "SERPENT": + blockCipher = new SerpentEngine(); + break; + case "SKIPJACK": + blockCipher = new SkipjackEngine(); + break; + case "TEA": + blockCipher = new TeaEngine(); + break; + case "TWOFISH": + blockCipher = new TwofishEngine(); + break; + case "VMPC": + streamCipher = new VmpcEngine(); + break; + case "VMPC-KSA3": + streamCipher = new VmpcKsa3Engine(); + break; + case "XTEA": + blockCipher = new XteaEngine(); + break; + default: + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + + if (streamCipher != null) + { + if (parts.Length > 1) + throw new ArgumentException("Modes and paddings not used for stream ciphers"); + + return new BufferedStreamCipher(streamCipher); + } + + + bool cts = false; + bool padded = true; + IBlockCipherPadding padding = null; + IAeadBlockCipher aeadBlockCipher = null; + + if (parts.Length > 2) + { + if (streamCipher != null) + throw new ArgumentException("Paddings not used for stream ciphers"); + + switch (parts[2]) + { + case "NOPADDING": + padded = false; + break; + case "": + case "RAW": + break; + case "ISO10126PADDING": + case "ISO10126D2PADDING": + case "ISO10126-2PADDING": + padding = new ISO10126d2Padding(); + break; + case "ISO7816-4PADDING": + case "ISO9797-1PADDING": + padding = new ISO7816d4Padding(); + break; + case "ISO9796-1": + case "ISO9796-1PADDING": + asymBlockCipher = new ISO9796d1Encoding(asymBlockCipher); + break; + case "OAEP": + case "OAEPPADDING": + asymBlockCipher = new OaepEncoding(asymBlockCipher); + break; + case "OAEPWITHMD5ANDMGF1PADDING": + asymBlockCipher = new OaepEncoding(asymBlockCipher, new MD5Digest()); + break; + case "OAEPWITHSHA1ANDMGF1PADDING": + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha1Digest()); + break; + case "OAEPWITHSHA224ANDMGF1PADDING": + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha224Digest()); + break; + case "OAEPWITHSHA256ANDMGF1PADDING": + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha256Digest()); + break; + case "OAEPWITHSHA384ANDMGF1PADDING": + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha384Digest()); + break; + case "OAEPWITHSHA512ANDMGF1PADDING": + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha512Digest()); + break; + case "PKCS1": + case "PKCS1PADDING": + asymBlockCipher = new Pkcs1Encoding(asymBlockCipher); + break; + case "PKCS5": + case "PKCS5PADDING": + case "PKCS7": + case "PKCS7PADDING": + // NB: Padding defaults to Pkcs7Padding already + break; + case "TBCPADDING": + padding = new TbcPadding(); + break; + case "WITHCTS": + cts = true; + break; + case "X9.23PADDING": + case "X923PADDING": + padding = new X923Padding(); + break; + case "ZEROBYTEPADDING": + padding = new ZeroBytePadding(); + break; + default: + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + } + + string mode = ""; + if (parts.Length > 1) + { + mode = parts[1]; + + int di = GetDigitIndex(mode); + string modeName = di >= 0 ? mode.Substring(0, di) : mode; + + switch (modeName) + { + case "": + case "ECB": + case "NONE": + break; + case "CBC": + blockCipher = new CbcBlockCipher(blockCipher); + break; + case "CCM": + aeadBlockCipher = new CcmBlockCipher(blockCipher); + break; + case "CFB": + { + int bits = (di < 0) + ? 8 * blockCipher.GetBlockSize() + : int.Parse(mode.Substring(di)); + + blockCipher = new CfbBlockCipher(blockCipher, bits); + break; + } + case "CTR": + blockCipher = new SicBlockCipher(blockCipher); + break; + case "CTS": + cts = true; + blockCipher = new CbcBlockCipher(blockCipher); + break; + case "EAX": + aeadBlockCipher = new EaxBlockCipher(blockCipher); + break; + case "GCM": + aeadBlockCipher = new GcmBlockCipher(blockCipher); + break; + case "GOFB": + blockCipher = new GOfbBlockCipher(blockCipher); + break; + case "OFB": + { + int bits = (di < 0) + ? 8 * blockCipher.GetBlockSize() + : int.Parse(mode.Substring(di)); + + blockCipher = new OfbBlockCipher(blockCipher, bits); + break; + } + case "OPENPGPCFB": + blockCipher = new OpenPgpCfbBlockCipher(blockCipher); + break; + case "SIC": + if (blockCipher.GetBlockSize() < 16) + { + throw new ArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)"); + } + blockCipher = new SicBlockCipher(blockCipher); + break; + default: + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + } + + if (aeadBlockCipher != null) + { + if (cts) + throw new SecurityUtilityException("CTS mode not valid for AEAD ciphers."); + if (padded && parts.Length > 1 && parts[2] != "") + throw new SecurityUtilityException("Bad padding specified for AEAD cipher."); + + return new BufferedAeadBlockCipher(aeadBlockCipher); + } + + if (blockCipher != null) + { + if (cts) + { + return new CtsBlockCipher(blockCipher); + } + + if (!padded || blockCipher.IsPartialBlockOkay) + { + return new BufferedBlockCipher(blockCipher); + } + + if (padding != null) + { + return new PaddedBufferedBlockCipher(blockCipher, padding); + } + + return new PaddedBufferedBlockCipher(blockCipher); + } + + if (asymBlockCipher != null) + { + return new BufferedAsymmetricBlockCipher(asymBlockCipher); + } + + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + private static int GetDigitIndex( + string s) + { + for (int i = 0; i < s.Length; ++i) + { + if (char.IsDigit(s[i])) + return i; + } + + return -1; + } + } +} diff --git a/src/core/srcbc/security/DigestUtilities.cs b/src/core/srcbc/security/DigestUtilities.cs new file mode 100644 index 0000000..1a0aea7 --- /dev/null +++ b/src/core/srcbc/security/DigestUtilities.cs @@ -0,0 +1,150 @@ +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Security +{ + + /// + /// Utility class for creating IDigest objects from their names/Oids + /// + public sealed class DigestUtilities + { + private DigestUtilities() + { + } + + private static readonly Hashtable algorithms = new Hashtable(); + private static readonly Hashtable oids = new Hashtable(); + + static DigestUtilities() + { + algorithms[PkcsObjectIdentifiers.MD2.Id] = "MD2"; + algorithms[PkcsObjectIdentifiers.MD4.Id] = "MD4"; + algorithms[PkcsObjectIdentifiers.MD5.Id] = "MD5"; + + algorithms["SHA1"] = "SHA-1"; + algorithms[OiwObjectIdentifiers.IdSha1.Id] = "SHA-1"; + algorithms["SHA224"] = "SHA-224"; + algorithms[NistObjectIdentifiers.IdSha224.Id] = "SHA-224"; + algorithms["SHA256"] = "SHA-256"; + algorithms[NistObjectIdentifiers.IdSha256.Id] = "SHA-256"; + algorithms["SHA384"] = "SHA-384"; + algorithms[NistObjectIdentifiers.IdSha384.Id] = "SHA-384"; + algorithms["SHA512"] = "SHA-512"; + algorithms[NistObjectIdentifiers.IdSha512.Id] = "SHA-512"; + + algorithms["RIPEMD-128"] = "RIPEMD128"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD128.Id] = "RIPEMD128"; + algorithms["RIPEMD-160"] = "RIPEMD160"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD160.Id] = "RIPEMD160"; + algorithms["RIPEMD-256"] = "RIPEMD256"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD256.Id] = "RIPEMD256"; + algorithms["RIPEMD-320"] = "RIPEMD320"; +// algorithms[TeleTrusTObjectIdentifiers.RipeMD320.Id] = "RIPEMD320"; + + algorithms[CryptoProObjectIdentifiers.GostR3411.Id] = "GOST3411"; + + + + oids["MD2"] = PkcsObjectIdentifiers.MD2; + oids["MD4"] = PkcsObjectIdentifiers.MD4; + oids["MD5"] = PkcsObjectIdentifiers.MD5; + oids["SHA-1"] = OiwObjectIdentifiers.IdSha1; + oids["SHA-224"] = NistObjectIdentifiers.IdSha224; + oids["SHA-256"] = NistObjectIdentifiers.IdSha256; + oids["SHA-384"] = NistObjectIdentifiers.IdSha384; + oids["SHA-512"] = NistObjectIdentifiers.IdSha512; + oids["RIPEMD128"] = TeleTrusTObjectIdentifiers.RipeMD128; + oids["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; + oids["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; + oids["GOST3411"] = CryptoProObjectIdentifiers.GostR3411; + } + + /// + /// Returns a ObjectIdentifier for a given digest mechanism. + /// + /// A string representation of the digest meanism. + /// A DerObjectIdentifier, null if the Oid is not available. + + public static DerObjectIdentifier GetObjectIdentifier( + string mechanism) + { + mechanism = (string) algorithms[mechanism.ToUpper(CultureInfo.InvariantCulture)]; + + if (mechanism != null) + { + return (DerObjectIdentifier)oids[mechanism]; + } + + return null; + } + + public static ICollection Algorithms + { + get { return oids.Keys; } + } + + public static IDigest GetDigest( + DerObjectIdentifier id) + { + return GetDigest(id.Id); + } + + public static IDigest GetDigest( + string algorithm) + { + string upper = algorithm.ToUpper(CultureInfo.InvariantCulture); + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + switch (mechanism) + { + case "GOST3411": return new Gost3411Digest(); + case "MD2": return new MD2Digest(); + case "MD4": return new MD4Digest(); + case "MD5": return new MD5Digest(); + case "RIPEMD128": return new RipeMD128Digest(); + case "RIPEMD160": return new RipeMD160Digest(); + case "RIPEMD256": return new RipeMD256Digest(); + case "RIPEMD320": return new RipeMD320Digest(); + case "SHA-1": return new Sha1Digest(); + case "SHA-224": return new Sha224Digest(); + case "SHA-256": return new Sha256Digest(); + case "SHA-384": return new Sha384Digest(); + case "SHA-512": return new Sha512Digest(); + case "TIGER": return new TigerDigest(); + case "WHIRLPOOL": return new WhirlpoolDigest(); + default: + throw new SecurityUtilityException("Digest " + mechanism + " not recognised."); + } + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + public static byte[] DoFinal( + IDigest digest) + { + byte[] b = new byte[digest.GetDigestSize()]; + digest.DoFinal(b, 0); + return b; + } + } +} diff --git a/src/core/srcbc/security/DotNetUtilities.cs b/src/core/srcbc/security/DotNetUtilities.cs new file mode 100644 index 0000000..32fb601 --- /dev/null +++ b/src/core/srcbc/security/DotNetUtilities.cs @@ -0,0 +1,163 @@ +#if !NETCF_1_0 + +using System; +using System.Security.Cryptography; +using SystemX509 = System.Security.Cryptography.X509Certificates; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Security +{ + /// + /// A class containing methods to interface the BouncyCastle world to the .NET Crypto world. + /// + public sealed class DotNetUtilities + { + private DotNetUtilities() + { + } + + /// + /// Create an System.Security.Cryptography.X509Certificate from an X509Certificate Structure. + /// + /// + /// A System.Security.Cryptography.X509Certificate. + public static SystemX509.X509Certificate ToX509Certificate( + X509CertificateStructure x509Struct) + { + return new SystemX509.X509Certificate(x509Struct.GetDerEncoded()); + } + + public static SystemX509.X509Certificate ToX509Certificate( + X509Certificate x509Cert) + { + return new SystemX509.X509Certificate(x509Cert.GetEncoded()); + } + + public static X509Certificate FromX509Certificate( + SystemX509.X509Certificate x509Cert) + { + return new X509CertificateParser().ReadCertificate(x509Cert.GetRawCertData()); + } + + public static AsymmetricCipherKeyPair GetDsaKeyPair( + DSACryptoServiceProvider dsaCsp) + { + return GetDsaKeyPair(dsaCsp.ExportParameters(true)); + } + + public static AsymmetricCipherKeyPair GetDsaKeyPair( + DSAParameters dp) + { + DsaValidationParameters validationParameters = (dp.Seed != null) + ? new DsaValidationParameters(dp.Seed, dp.Counter) + : null; + + DsaParameters parameters = new DsaParameters( + new BigInteger(1, dp.P), + new BigInteger(1, dp.Q), + new BigInteger(1, dp.G), + validationParameters); + + DsaPublicKeyParameters pubKey = new DsaPublicKeyParameters( + new BigInteger(1, dp.Y), + parameters); + + DsaPrivateKeyParameters privKey = new DsaPrivateKeyParameters( + new BigInteger(1, dp.X), + parameters); + + return new AsymmetricCipherKeyPair(pubKey, privKey); + } + + public static DsaPublicKeyParameters GetDsaPublicKey( + DSACryptoServiceProvider dsaCsp) + { + return GetDsaPublicKey(dsaCsp.ExportParameters(false)); + } + + public static DsaPublicKeyParameters GetDsaPublicKey( + DSAParameters dp) + { + DsaValidationParameters validationParameters = (dp.Seed != null) + ? new DsaValidationParameters(dp.Seed, dp.Counter) + : null; + + DsaParameters parameters = new DsaParameters( + new BigInteger(1, dp.P), + new BigInteger(1, dp.Q), + new BigInteger(1, dp.G), + validationParameters); + + return new DsaPublicKeyParameters( + new BigInteger(1, dp.Y), + parameters); + } + + public static AsymmetricCipherKeyPair GetRsaKeyPair( + RSACryptoServiceProvider rsaCsp) + { + return GetRsaKeyPair(rsaCsp.ExportParameters(true)); + } + + public static AsymmetricCipherKeyPair GetRsaKeyPair( + RSAParameters rp) + { + BigInteger modulus = new BigInteger(1, rp.Modulus); + BigInteger pubExp = new BigInteger(1, rp.Exponent); + + RsaKeyParameters pubKey = new RsaKeyParameters( + false, + modulus, + pubExp); + + RsaPrivateCrtKeyParameters privKey = new RsaPrivateCrtKeyParameters( + modulus, + pubExp, + new BigInteger(1, rp.D), + new BigInteger(1, rp.P), + new BigInteger(1, rp.Q), + new BigInteger(1, rp.DP), + new BigInteger(1, rp.DQ), + new BigInteger(1, rp.InverseQ)); + + return new AsymmetricCipherKeyPair(pubKey, privKey); + } + + public static RsaKeyParameters GetRsaPublicKey( + RSACryptoServiceProvider rsaCsp) + { + return GetRsaPublicKey(rsaCsp.ExportParameters(false)); + } + + public static RsaKeyParameters GetRsaPublicKey( + RSAParameters rp) + { + return new RsaKeyParameters( + false, + new BigInteger(1, rp.Modulus), + new BigInteger(1, rp.Exponent)); + } + + public static AsymmetricCipherKeyPair GetKeyPair(AsymmetricAlgorithm privateKey) + { + if (privateKey is DSACryptoServiceProvider) + { + return GetDsaKeyPair((DSACryptoServiceProvider) privateKey); + } + + if (privateKey is RSACryptoServiceProvider) + { + return GetRsaKeyPair((RSACryptoServiceProvider) privateKey); + } + + throw new ArgumentException("Unsupported algorithm specified", "privateKey"); + } + } +} + +#endif diff --git a/src/core/srcbc/security/GeneralSecurityException.cs b/src/core/srcbc/security/GeneralSecurityException.cs new file mode 100644 index 0000000..e22bb20 --- /dev/null +++ b/src/core/srcbc/security/GeneralSecurityException.cs @@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class GeneralSecurityException + : Exception + { + public GeneralSecurityException() + : base() + { + } + + public GeneralSecurityException( + string message) + : base(message) + { + } + + public GeneralSecurityException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/src/core/srcbc/security/GeneratorUtilities.cs b/src/core/srcbc/security/GeneratorUtilities.cs new file mode 100644 index 0000000..9e9df9a --- /dev/null +++ b/src/core/srcbc/security/GeneratorUtilities.cs @@ -0,0 +1,352 @@ +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Iana; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; + +namespace Org.BouncyCastle.Security +{ + public sealed class GeneratorUtilities + { + private GeneratorUtilities() + { + } + + private static readonly Hashtable kgAlgorithms = new Hashtable(); + private static readonly Hashtable kpgAlgorithms = new Hashtable(); + + static GeneratorUtilities() + { + // + // key generators. + // + AddKgAlgorithm("AES", + "AESWRAP"); + AddKgAlgorithm("AES128", + "2.16.840.1.101.3.4.2", + NistObjectIdentifiers.IdAes128Cbc, + NistObjectIdentifiers.IdAes128Cfb, + NistObjectIdentifiers.IdAes128Ecb, + NistObjectIdentifiers.IdAes128Ofb, + NistObjectIdentifiers.IdAes128Wrap); + AddKgAlgorithm("AES192", + "2.16.840.1.101.3.4.22", + NistObjectIdentifiers.IdAes192Cbc, + NistObjectIdentifiers.IdAes192Cfb, + NistObjectIdentifiers.IdAes192Ecb, + NistObjectIdentifiers.IdAes192Ofb, + NistObjectIdentifiers.IdAes192Wrap); + AddKgAlgorithm("AES256", + "2.16.840.1.101.3.4.42", + NistObjectIdentifiers.IdAes256Cbc, + NistObjectIdentifiers.IdAes256Cfb, + NistObjectIdentifiers.IdAes256Ecb, + NistObjectIdentifiers.IdAes256Ofb, + NistObjectIdentifiers.IdAes256Wrap); + AddKgAlgorithm("BLOWFISH"); + AddKgAlgorithm("CAMELLIA", + "CAMELLIAWRAP"); + AddKgAlgorithm("CAMELLIA128", + NttObjectIdentifiers.IdCamellia128Cbc, + NttObjectIdentifiers.IdCamellia128Wrap); + AddKgAlgorithm("CAMELLIA192", + NttObjectIdentifiers.IdCamellia192Cbc, + NttObjectIdentifiers.IdCamellia192Wrap); + AddKgAlgorithm("CAMELLIA256", + NttObjectIdentifiers.IdCamellia256Cbc, + NttObjectIdentifiers.IdCamellia256Wrap); + AddKgAlgorithm("CAST5", + "1.2.840.113533.7.66.10"); + AddKgAlgorithm("CAST6"); + AddKgAlgorithm("DES", + OiwObjectIdentifiers.DesCbc); + AddKgAlgorithm("DESEDE", + "DESEDEWRAP", + PkcsObjectIdentifiers.IdAlgCms3DesWrap); + AddKgAlgorithm("DESEDE3", + PkcsObjectIdentifiers.DesEde3Cbc); + AddKgAlgorithm("GOST28147", + "GOST", + "GOST-28147", + CryptoProObjectIdentifiers.GostR28147Cbc); + AddKgAlgorithm("HC128"); + AddKgAlgorithm("HC256"); + AddKgAlgorithm("IDEA", + "1.3.6.1.4.1.188.7.1.1.2"); + AddKgAlgorithm("NOEKEON"); + AddKgAlgorithm("RC2", + PkcsObjectIdentifiers.RC2Cbc, + PkcsObjectIdentifiers.IdAlgCmsRC2Wrap); + AddKgAlgorithm("RC4", + "ARC4", + "1.2.840.113549.3.4"); + AddKgAlgorithm("RC5", + "RC5-32"); + AddKgAlgorithm("RC5-64"); + AddKgAlgorithm("RC6"); + AddKgAlgorithm("RIJNDAEL"); + AddKgAlgorithm("SALSA20"); + AddKgAlgorithm("SEED", + KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap, + KisaObjectIdentifiers.IdSeedCbc); + AddKgAlgorithm("SERPENT"); + AddKgAlgorithm("SKIPJACK"); + AddKgAlgorithm("TEA"); + AddKgAlgorithm("TWOFISH"); + AddKgAlgorithm("VMPC"); + AddKgAlgorithm("VMPC-KSA3"); + AddKgAlgorithm("XTEA"); + + // + // HMac key generators + // + AddHMacKeyGenerator("MD2"); + AddHMacKeyGenerator("MD4"); + AddHMacKeyGenerator("MD5", + IanaObjectIdentifiers.HmacMD5); + AddHMacKeyGenerator("SHA1", + PkcsObjectIdentifiers.IdHmacWithSha1, + IanaObjectIdentifiers.HmacSha1); + AddHMacKeyGenerator("SHA224", + PkcsObjectIdentifiers.IdHmacWithSha224); + AddHMacKeyGenerator("SHA256", + PkcsObjectIdentifiers.IdHmacWithSha256); + AddHMacKeyGenerator("SHA384", + PkcsObjectIdentifiers.IdHmacWithSha384); + AddHMacKeyGenerator("SHA512", + PkcsObjectIdentifiers.IdHmacWithSha512); + AddHMacKeyGenerator("RIPEMD128"); + AddHMacKeyGenerator("RIPEMD160", + IanaObjectIdentifiers.HmacRipeMD160); + AddHMacKeyGenerator("TIGER", + IanaObjectIdentifiers.HmacTiger); + + + + // + // key pair generators. + // + AddKpgAlgorithm("DH"); + AddKpgAlgorithm("DSA"); + AddKpgAlgorithm("EC"); + AddKpgAlgorithm("ECDH", + "ECIES"); + AddKpgAlgorithm("ECDHC"); + AddKpgAlgorithm("ECDSA"); + AddKpgAlgorithm("ECGOST3410", + "ECGOST-3410", + "GOST-3410-2001"); + AddKpgAlgorithm("ELGAMAL"); + AddKpgAlgorithm("GOST3410", + "GOST-3410", + "GOST-3410-94"); + AddKpgAlgorithm("RSA", + "1.2.840.113549.1.1.1"); + } + + private static void AddKgAlgorithm( + string canonicalName, + params object[] aliases) + { + kgAlgorithms[canonicalName] = canonicalName; + + foreach (object alias in aliases) + { + kgAlgorithms[alias.ToString()] = canonicalName; + } + } + + private static void AddKpgAlgorithm( + string canonicalName, + params object[] aliases) + { + kpgAlgorithms[canonicalName] = canonicalName; + + foreach (object alias in aliases) + { + kpgAlgorithms[alias.ToString()] = canonicalName; + } + } + + private static void AddHMacKeyGenerator( + string algorithm, + params object[] aliases) + { + string mainName = "HMAC" + algorithm; + + kgAlgorithms[mainName] = mainName; + kgAlgorithms["HMAC-" + algorithm] = mainName; + kgAlgorithms["HMAC/" + algorithm] = mainName; + + foreach (object alias in aliases) + { + kgAlgorithms[alias.ToString()] = mainName; + } + } + + // TODO Consider making this public + internal static string GetCanonicalKeyGeneratorAlgorithm( + string algorithm) + { + return (string) kgAlgorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + } + + // TODO Consider making this public + internal static string GetCanonicalKeyPairGeneratorAlgorithm( + string algorithm) + { + return (string) kpgAlgorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + } + + public static CipherKeyGenerator GetKeyGenerator( + DerObjectIdentifier oid) + { + return GetKeyGenerator(oid.Id); + } + + public static CipherKeyGenerator GetKeyGenerator( + string algorithm) + { + string canonicalName = GetCanonicalKeyGeneratorAlgorithm(algorithm); + + if (canonicalName == null) + throw new SecurityUtilityException("KeyGenerator " + algorithm + " not recognised."); + + switch (canonicalName) + { + case "DES": + return new DesKeyGenerator(64); + case "DESEDE": + return new DesEdeKeyGenerator(128); + case "DESEDE3": + return new DesEdeKeyGenerator(192); + case "AES": + return new CipherKeyGenerator(192); + case "AES128": + return new CipherKeyGenerator(128); + case "AES192": + return new CipherKeyGenerator(192); + case "AES256": + return new CipherKeyGenerator(256); + case "BLOWFISH": + return new CipherKeyGenerator(448); + case "CAMELLIA": + return new CipherKeyGenerator(256); + case "CAMELLIA128": + return new CipherKeyGenerator(128); + case "CAMELLIA192": + return new CipherKeyGenerator(192); + case "CAMELLIA256": + return new CipherKeyGenerator(256); + case "CAST5": + return new CipherKeyGenerator(128); + case "CAST6": + return new CipherKeyGenerator(256); + case "GOST28147": + return new CipherKeyGenerator(256); + case "HC128": + return new CipherKeyGenerator(128); + case "HC256": + return new CipherKeyGenerator(256); + case "HMACMD2": + case "HMACMD4": + case "HMACMD5": + return new CipherKeyGenerator(128); + case "HMACSHA1": + return new CipherKeyGenerator(160); + case "HMACSHA224": + return new CipherKeyGenerator(224); + case "HMACSHA256": + return new CipherKeyGenerator(256); + case "HMACSHA384": + return new CipherKeyGenerator(384); + case "HMACSHA512": + return new CipherKeyGenerator(512); + case "HMACRIPEMD128": + return new CipherKeyGenerator(128); + case "HMACRIPEMD160": + return new CipherKeyGenerator(160); + case "HMACTIGER": + return new CipherKeyGenerator(192); + case "IDEA": + return new CipherKeyGenerator(128); + case "NOEKEON": + return new CipherKeyGenerator(128); + case "RC2": + case "RC4": + case "RC5": + return new CipherKeyGenerator(128); + case "RC5-64": + case "RC6": + return new CipherKeyGenerator(256); + case "RIJNDAEL": + return new CipherKeyGenerator(192); + case "SALSA20": + return new CipherKeyGenerator(128); + case "SEED": + return new CipherKeyGenerator(128); + case "SERPENT": + return new CipherKeyGenerator(192); + case "SKIPJACK": + return new CipherKeyGenerator(80); + case "TEA": + case "XTEA": + return new CipherKeyGenerator(128); + case "TWOFISH": + return new CipherKeyGenerator(256); + case "VMPC": + case "VMPC-KSA3": + return new CipherKeyGenerator(128); + } + + throw new SecurityUtilityException("KeyGenerator " + algorithm + " not recognised."); + } + + public static IAsymmetricCipherKeyPairGenerator GetKeyPairGenerator( + DerObjectIdentifier oid) + { + return GetKeyPairGenerator(oid.Id); + } + + public static IAsymmetricCipherKeyPairGenerator GetKeyPairGenerator( + string algorithm) + { + string canonicalName = GetCanonicalKeyPairGeneratorAlgorithm(algorithm); + + if (canonicalName == null) + throw new SecurityUtilityException("KeyPairGenerator " + algorithm + " not recognised."); + + switch (canonicalName) + { + case "DH": + return new DHKeyPairGenerator(); + case "DSA": + return new DsaKeyPairGenerator(); + case "EC": + case "ECDH": + case "ECDHC": + case "ECDSA": + case "ECGOST3410": + return new ECKeyPairGenerator(canonicalName); + case "ELGAMAL": + return new ElGamalKeyPairGenerator(); + case "GOST3410": + return new Gost3410KeyPairGenerator(); + case "RSA": + return new RsaKeyPairGenerator(); + default: + break; + } + + throw new SecurityUtilityException("KeyPairGenerator " + algorithm + " not recognised."); + } + } +} diff --git a/src/core/srcbc/security/InvalidKeyException.cs b/src/core/srcbc/security/InvalidKeyException.cs new file mode 100644 index 0000000..570188f --- /dev/null +++ b/src/core/srcbc/security/InvalidKeyException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class InvalidKeyException : KeyException + { + public InvalidKeyException() : base() { } + public InvalidKeyException(string message) : base(message) { } + public InvalidKeyException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/security/InvalidParameterException.cs b/src/core/srcbc/security/InvalidParameterException.cs new file mode 100644 index 0000000..ea7b82f --- /dev/null +++ b/src/core/srcbc/security/InvalidParameterException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class InvalidParameterException : KeyException + { + public InvalidParameterException() : base() { } + public InvalidParameterException(string message) : base(message) { } + public InvalidParameterException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/security/KeyException.cs b/src/core/srcbc/security/KeyException.cs new file mode 100644 index 0000000..d7a4099 --- /dev/null +++ b/src/core/srcbc/security/KeyException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class KeyException : GeneralSecurityException + { + public KeyException() : base() { } + public KeyException(string message) : base(message) { } + public KeyException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/security/MacUtilities.cs b/src/core/srcbc/security/MacUtilities.cs new file mode 100644 index 0000000..d227c03 --- /dev/null +++ b/src/core/srcbc/security/MacUtilities.cs @@ -0,0 +1,220 @@ +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Iana; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Security +{ + /// + /// Utility class for creating HMac object from their names/Oids + /// + public sealed class MacUtilities + { + private MacUtilities() + { + } + + private static readonly Hashtable algorithms = new Hashtable(); +// private static readonly Hashtable oids = new Hashtable(); + + static MacUtilities() + { + algorithms[IanaObjectIdentifiers.HmacMD5.Id] = "HMAC-MD5"; + algorithms[IanaObjectIdentifiers.HmacRipeMD160.Id] = "HMAC-RIPEMD160"; + algorithms[IanaObjectIdentifiers.HmacSha1.Id] = "HMAC-SHA1"; + algorithms[IanaObjectIdentifiers.HmacTiger.Id] = "HMAC-TIGER"; + + algorithms[PkcsObjectIdentifiers.IdHmacWithSha1.Id] = "HMAC-SHA1"; + algorithms[PkcsObjectIdentifiers.IdHmacWithSha224.Id] = "HMAC-SHA224"; + algorithms[PkcsObjectIdentifiers.IdHmacWithSha256.Id] = "HMAC-SHA256"; + algorithms[PkcsObjectIdentifiers.IdHmacWithSha384.Id] = "HMAC-SHA384"; + algorithms[PkcsObjectIdentifiers.IdHmacWithSha512.Id] = "HMAC-SHA512"; + + algorithms["DES"] = "DESMAC"; + algorithms["DES/CFB8"] = "DESMAC/CFB8"; + algorithms["DESEDE"] = "DESEDEMAC"; + algorithms["DESEDE/CFB8"] = "DESEDEMAC/CFB8"; + algorithms["DESISO9797MAC"] = "DESWITHISO9797"; + algorithms["DESEDE64"] = "DESEDEMAC64"; + + algorithms["DESEDE64WITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING"; + algorithms["DESEDEISO9797ALG1MACWITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING"; + algorithms["DESEDEISO9797ALG1WITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING"; + + algorithms["ISO9797ALG3"] = "ISO9797ALG3MAC"; + algorithms["ISO9797ALG3MACWITHISO7816-4PADDING"] = "ISO9797ALG3WITHISO7816-4PADDING"; + + algorithms["SKIPJACK"] = "SKIPJACKMAC"; + algorithms["SKIPJACK/CFB8"] = "SKIPJACKMAC/CFB8"; + algorithms["IDEA"] = "IDEAMAC"; + algorithms["IDEA/CFB8"] = "IDEAMAC/CFB8"; + algorithms["RC2"] = "RC2MAC"; + algorithms["RC2/CFB8"] = "RC2MAC/CFB8"; + algorithms["RC5"] = "RC5MAC"; + algorithms["RC5/CFB8"] = "RC5MAC/CFB8"; + algorithms["GOST28147"] = "GOST28147MAC"; + algorithms["VMPC"] = "VMPCMAC"; + algorithms["VMPC-MAC"] = "VMPCMAC"; + + algorithms["PBEWITHHMACSHA"] = "PBEWITHHMACSHA1"; + algorithms["1.3.14.3.2.26"] = "PBEWITHHMACSHA1"; + } + +// /// +// /// Returns a ObjectIdentifier for a given digest mechanism. +// /// +// /// A string representation of the digest meanism. +// /// A DerObjectIdentifier, null if the Oid is not available. +// public static DerObjectIdentifier GetObjectIdentifier( +// string mechanism) +// { +// mechanism = (string) algorithms[mechanism.ToUpper(CultureInfo.InvariantCulture)]; +// +// if (mechanism != null) +// { +// return (DerObjectIdentifier)oids[mechanism]; +// } +// +// return null; +// } + +// public static ICollection Algorithms +// { +// get { return oids.Keys; } +// } + + public static IMac GetMac( + DerObjectIdentifier id) + { + return GetMac(id.Id); + } + + public static IMac GetMac( + string algorithm) + { + string upper = algorithm.ToUpper(CultureInfo.InvariantCulture); + + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + if (mechanism.StartsWith("PBEWITH")) + { + mechanism = mechanism.Substring("PBEWITH".Length); + } + + if (mechanism.StartsWith("HMAC")) + { + string digestName; + if (mechanism.StartsWith("HMAC-") || mechanism.StartsWith("HMAC/")) + { + digestName = mechanism.Substring(5); + } + else + { + digestName = mechanism.Substring(4); + } + + return new HMac(DigestUtilities.GetDigest(digestName)); + } + + if (mechanism == "DESMAC") + { + return new CbcBlockCipherMac(new DesEngine()); + } + if (mechanism == "DESMAC/CFB8") + { + return new CfbBlockCipherMac(new DesEngine()); + } + if (mechanism == "DESEDEMAC") + { + return new CbcBlockCipherMac(new DesEdeEngine()); + } + if (mechanism == "DESEDEMAC/CFB8") + { + return new CfbBlockCipherMac(new DesEdeEngine()); + } + if (mechanism == "DESEDEMAC64") + { + return new CbcBlockCipherMac(new DesEdeEngine(), 64); + } + if (mechanism == "DESEDEMAC64WITHISO7816-4PADDING") + { + return new CbcBlockCipherMac(new DesEdeEngine(), 64, new ISO7816d4Padding()); + } + if (mechanism == "DESWITHISO9797" + || mechanism == "ISO9797ALG3MAC") + { + return new ISO9797Alg3Mac(new DesEngine()); + } + if (mechanism == "ISO9797ALG3WITHISO7816-4PADDING") + { + return new ISO9797Alg3Mac(new DesEngine(), new ISO7816d4Padding()); + } + if (mechanism == "SKIPJACKMAC") + { + return new CbcBlockCipherMac(new SkipjackEngine()); + } + if (mechanism == "SKIPJACKMAC/CFB8") + { + return new CfbBlockCipherMac(new SkipjackEngine()); + } + if (mechanism == "IDEAMAC") + { + return new CbcBlockCipherMac(new IdeaEngine()); + } + if (mechanism == "IDEAMAC/CFB8") + { + return new CfbBlockCipherMac(new IdeaEngine()); + } + if (mechanism == "RC2MAC") + { + return new CbcBlockCipherMac(new RC2Engine()); + } + if (mechanism == "RC2MAC/CFB8") + { + return new CfbBlockCipherMac(new RC2Engine()); + } + if (mechanism == "RC5MAC") + { + return new CbcBlockCipherMac(new RC532Engine()); + } + if (mechanism == "RC5MAC/CFB8") + { + return new CfbBlockCipherMac(new RC532Engine()); + } + if (mechanism == "GOST28147MAC") + { + return new Gost28147Mac(); + } + if (mechanism == "VMPCMAC") + { + return new VmpcMac(); + } + throw new SecurityUtilityException("Mac " + mechanism + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + public static byte[] DoFinal( + IMac mac) + { + byte[] b = new byte[mac.GetMacSize()]; + mac.DoFinal(b, 0); + return b; + } + } +} diff --git a/src/core/srcbc/security/NoSuchAlgorithmException.cs b/src/core/srcbc/security/NoSuchAlgorithmException.cs new file mode 100644 index 0000000..1487556 --- /dev/null +++ b/src/core/srcbc/security/NoSuchAlgorithmException.cs @@ -0,0 +1,12 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + [Obsolete("Never thrown")] + public class NoSuchAlgorithmException : GeneralSecurityException + { + public NoSuchAlgorithmException() : base() {} + public NoSuchAlgorithmException(string message) : base(message) {} + public NoSuchAlgorithmException(string message, Exception exception) : base(message, exception) {} + } +} diff --git a/src/core/srcbc/security/ParameterUtilities.cs b/src/core/srcbc/security/ParameterUtilities.cs new file mode 100644 index 0000000..5abbc13 --- /dev/null +++ b/src/core/srcbc/security/ParameterUtilities.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Misc; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Security +{ + public sealed class ParameterUtilities + { + private ParameterUtilities() + { + } + + private static readonly Hashtable algorithms = new Hashtable(); + + static ParameterUtilities() + { + AddAlgorithm("AES", + "AESWRAP"); + AddAlgorithm("AES128", + "2.16.840.1.101.3.4.2", + NistObjectIdentifiers.IdAes128Cbc, + NistObjectIdentifiers.IdAes128Cfb, + NistObjectIdentifiers.IdAes128Ecb, + NistObjectIdentifiers.IdAes128Ofb, + NistObjectIdentifiers.IdAes128Wrap); + AddAlgorithm("AES192", + "2.16.840.1.101.3.4.22", + NistObjectIdentifiers.IdAes192Cbc, + NistObjectIdentifiers.IdAes192Cfb, + NistObjectIdentifiers.IdAes192Ecb, + NistObjectIdentifiers.IdAes192Ofb, + NistObjectIdentifiers.IdAes192Wrap); + AddAlgorithm("AES256", + "2.16.840.1.101.3.4.42", + NistObjectIdentifiers.IdAes256Cbc, + NistObjectIdentifiers.IdAes256Cfb, + NistObjectIdentifiers.IdAes256Ecb, + NistObjectIdentifiers.IdAes256Ofb, + NistObjectIdentifiers.IdAes256Wrap); + AddAlgorithm("BLOWFISH"); + AddAlgorithm("CAMELLIA", + "CAMELLIAWRAP"); + AddAlgorithm("CAMELLIA128", + NttObjectIdentifiers.IdCamellia128Cbc, + NttObjectIdentifiers.IdCamellia128Wrap); + AddAlgorithm("CAMELLIA192", + NttObjectIdentifiers.IdCamellia192Cbc, + NttObjectIdentifiers.IdCamellia192Wrap); + AddAlgorithm("CAMELLIA256", + NttObjectIdentifiers.IdCamellia256Cbc, + NttObjectIdentifiers.IdCamellia256Wrap); + AddAlgorithm("CAST5", + "1.2.840.113533.7.66.10"); + AddAlgorithm("CAST6"); + AddAlgorithm("DES", + OiwObjectIdentifiers.DesCbc); + AddAlgorithm("DESEDE", + "DESEDEWRAP", + PkcsObjectIdentifiers.IdAlgCms3DesWrap); + AddAlgorithm("DESEDE3", + PkcsObjectIdentifiers.DesEde3Cbc); + AddAlgorithm("GOST28147", + "GOST", + "GOST-28147", + CryptoProObjectIdentifiers.GostR28147Cbc); + AddAlgorithm("HC128"); + AddAlgorithm("HC256"); + AddAlgorithm("IDEA", + "1.3.6.1.4.1.188.7.1.1.2"); + AddAlgorithm("NOEKEON"); + AddAlgorithm("RC2", + PkcsObjectIdentifiers.RC2Cbc, + PkcsObjectIdentifiers.IdAlgCmsRC2Wrap); + AddAlgorithm("RC4", + "ARC4", + "1.2.840.113549.3.4"); + AddAlgorithm("RC5", + "RC5-32"); + AddAlgorithm("RC5-64"); + AddAlgorithm("RC6"); + AddAlgorithm("RIJNDAEL"); + AddAlgorithm("SALSA20"); + AddAlgorithm("SEED", + KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap, + KisaObjectIdentifiers.IdSeedCbc); + AddAlgorithm("SERPENT"); + AddAlgorithm("SKIPJACK"); + AddAlgorithm("TEA"); + AddAlgorithm("TWOFISH"); + AddAlgorithm("VMPC"); + AddAlgorithm("VMPC-KSA3"); + AddAlgorithm("XTEA"); + } + + private static void AddAlgorithm( + string canonicalName, + params object[] aliases) + { + algorithms[canonicalName] = canonicalName; + + foreach (object alias in aliases) + { + algorithms[alias.ToString()] = canonicalName; + } + } + + public static string GetCanonicalAlgorithmName( + string algorithm) + { + return (string) algorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + } + + public static KeyParameter CreateKeyParameter( + DerObjectIdentifier algOid, + byte[] keyBytes) + { + return CreateKeyParameter(algOid.Id, keyBytes, 0, keyBytes.Length); + } + + public static KeyParameter CreateKeyParameter( + string algorithm, + byte[] keyBytes) + { + return CreateKeyParameter(algorithm, keyBytes, 0, keyBytes.Length); + } + + public static KeyParameter CreateKeyParameter( + DerObjectIdentifier algOid, + byte[] keyBytes, + int offset, + int length) + { + return CreateKeyParameter(algOid.Id, keyBytes, offset, length); + } + + public static KeyParameter CreateKeyParameter( + string algorithm, + byte[] keyBytes, + int offset, + int length) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + string canonical = GetCanonicalAlgorithmName(algorithm); + + if (canonical == null) + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + + switch (canonical) + { + case "DES": + return new DesParameters(keyBytes, offset, length); + case "DESEDE": + case "DESEDE3": + return new DesEdeParameters(keyBytes, offset, length); + case "RC2": + return new RC2Parameters(keyBytes, offset, length); + default: + return new KeyParameter(keyBytes, offset, length); + } + } + + public static ICipherParameters GetCipherParameters( + DerObjectIdentifier algOid, + ICipherParameters key, + Asn1Object asn1Params) + { + return GetCipherParameters(algOid.Id, key, asn1Params); + } + + public static ICipherParameters GetCipherParameters( + string algorithm, + ICipherParameters key, + Asn1Object asn1Params) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + string canonical = GetCanonicalAlgorithmName(algorithm); + + if (canonical == null) + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + + byte[] iv = null; + + try + { + switch (canonical) + { + case "AES": + case "AES128": + case "AES192": + case "AES256": + case "BLOWFISH": + case "CAMELLIA": + case "CAMELLIA128": + case "CAMELLIA192": + case "CAMELLIA256": + case "DES": + case "DESEDE": + case "DESEDE3": + case "NOEKEON": + case "RIJNDAEL": + case "SEED": + case "SKIPJACK": + case "TWOFISH": + iv = ((Asn1OctetString) asn1Params).GetOctets(); + break; + case "RC2": + iv = RC2CbcParameter.GetInstance(asn1Params).GetIV(); + break; + case "IDEA": + iv = IdeaCbcPar.GetInstance(asn1Params).GetIV(); + break; + case "CAST5": + iv = Cast5CbcParameters.GetInstance(asn1Params).GetIV(); + break; + } + } + catch (Exception e) + { + throw new ArgumentException("Could not process ASN.1 parameters", e); + } + + if (iv != null) + { + return new ParametersWithIV(key, iv); + } + + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + } + + public static Asn1Encodable GenerateParameters( + DerObjectIdentifier algID, + SecureRandom random) + { + return GenerateParameters(algID.Id, random); + } + + public static Asn1Encodable GenerateParameters( + string algorithm, + SecureRandom random) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + string canonical = GetCanonicalAlgorithmName(algorithm); + + if (canonical == null) + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + + switch (canonical) + { + // TODO These algorithms support an IV (see GetCipherParameters) + // but JCE doesn't seem to provide an AlgorithmParametersGenerator for them +// case "BLOWFISH": +// case "RIJNDAEL": +// case "SKIPJACK": +// case "TWOFISH": + + case "AES": + case "AES128": + case "AES192": + case "AES256": + return CreateIVOctetString(random, 16); + case "CAMELLIA": + case "CAMELLIA128": + case "CAMELLIA192": + case "CAMELLIA256": + return CreateIVOctetString(random, 16); + case "CAST5": + return new Cast5CbcParameters(CreateIV(random, 8), 128); + case "DES": + case "DESEDE": + case "DESEDE3": + return CreateIVOctetString(random, 8); + case "IDEA": + return new IdeaCbcPar(CreateIV(random, 8)); + case "NOEKEON": + return CreateIVOctetString(random, 16); + case "RC2": + return new RC2CbcParameter(CreateIV(random, 8)); + case "SEED": + return CreateIVOctetString(random, 16); + } + + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + } + + private static Asn1OctetString CreateIVOctetString( + SecureRandom random, + int ivLength) + { + return new DerOctetString(CreateIV(random, ivLength)); + } + + private static byte[] CreateIV( + SecureRandom random, + int ivLength) + { + byte[] iv = new byte[ivLength]; + random.NextBytes(iv); + return iv; + } + } +} diff --git a/src/core/srcbc/security/PbeUtilities.cs b/src/core/srcbc/security/PbeUtilities.cs new file mode 100644 index 0000000..3717137 --- /dev/null +++ b/src/core/srcbc/security/PbeUtilities.cs @@ -0,0 +1,554 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Security +{ + /// + /// + /// + public sealed class PbeUtilities + { + private PbeUtilities() + { + } + + const string Pkcs5S1 = "Pkcs5S1"; + const string Pkcs5S2 = "Pkcs5S2"; + const string Pkcs12 = "Pkcs12"; + const string OpenSsl = "OpenSsl"; + + private static readonly Hashtable algorithms = new Hashtable(); + private static readonly Hashtable algorithmType = new Hashtable(); + private static readonly Hashtable oids = new Hashtable(); + + static PbeUtilities() + { + algorithms["PKCS5SCHEME1"] = "Pkcs5scheme1"; + algorithms["PKCS5SCHEME2"] = "Pkcs5scheme2"; + algorithms["PBEWITHMD2ANDDES-CBC"] = "PBEwithMD2andDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithMD2AndDesCbc.Id] = "PBEwithMD2andDES-CBC"; + algorithms["PBEWITHMD2ANDRC2-CBC"] = "PBEwithMD2andRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithMD2AndRC2Cbc.Id] = "PBEwithMD2andRC2-CBC"; + algorithms["PBEWITHMD5ANDDES-CBC"] = "PBEwithMD5andDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithMD5AndDesCbc.Id] = "PBEwithMD5andDES-CBC"; + algorithms["PBEWITHMD5ANDRC2-CBC"] = "PBEwithMD5andRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithMD5AndRC2Cbc.Id] = "PBEwithMD5andRC2-CBC"; + algorithms["PBEWITHSHA1ANDDES"] = "PBEwithSHA-1andDES-CBC"; + algorithms["PBEWITHSHA-1ANDDES"] = "PBEwithSHA-1andDES-CBC"; + algorithms["PBEWITHSHA1ANDDES-CBC"] = "PBEwithSHA-1andDES-CBC"; + algorithms["PBEWITHSHA-1ANDDES-CBC"] = "PBEwithSHA-1andDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithSha1AndDesCbc.Id] = "PBEwithSHA-1andDES-CBC"; + algorithms["PBEWITHSHA1ANDRC2"] = "PBEwithSHA-1andRC2-CBC"; + algorithms["PBEWITHSHA-1ANDRC2"] = "PBEwithSHA-1andRC2-CBC"; + algorithms["PBEWITHSHA1ANDRC2-CBC"] = "PBEwithSHA-1andRC2-CBC"; + algorithms["PBEWITHSHA-1ANDRC2-CBC"] = "PBEwithSHA-1andRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithSha1AndRC2Cbc.Id] = "PBEwithSHA-1andRC2-CBC"; + algorithms["PKCS12"] = "Pkcs12"; + algorithms["PBEWITHSHAAND128BITRC4"] = "PBEwithSHA-1and128bitRC4"; + algorithms["PBEWITHSHA1AND128BITRC4"] = "PBEwithSHA-1and128bitRC4"; + algorithms["PBEWITHSHA-1AND128BITRC4"] = "PBEwithSHA-1and128bitRC4"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd128BitRC4.Id] = "PBEwithSHA-1and128bitRC4"; + algorithms["PBEWITHSHAAND40BITRC4"] = "PBEwithSHA-1and40bitRC4"; + algorithms["PBEWITHSHA1AND40BITRC4"] = "PBEwithSHA-1and40bitRC4"; + algorithms["PBEWITHSHA-1AND40BITRC4"] = "PBEwithSHA-1and40bitRC4"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd40BitRC4.Id] = "PBEwithSHA-1and40bitRC4"; + algorithms["PBEWITHSHAAND3-KEYDESEDE-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHAAND3-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHA1AND3-KEYDESEDE-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHA1AND3-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHA-1AND3-KEYDESEDE-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHA-1AND3-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc.Id] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHAAND2-KEYDESEDE-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHAAND2-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHA1AND2-KEYDESEDE-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHA1AND2-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHA-1AND2-KEYDESEDE-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHA-1AND2-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd2KeyTripleDesCbc.Id] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHAAND128BITRC2-CBC"] = "PBEwithSHA-1and128bitRC2-CBC"; + algorithms["PBEWITHSHA1AND128BITRC2-CBC"] = "PBEwithSHA-1and128bitRC2-CBC"; + algorithms["PBEWITHSHA-1AND128BITRC2-CBC"] = "PBEwithSHA-1and128bitRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd128BitRC2Cbc.Id] = "PBEwithSHA-1and128bitRC2-CBC"; + algorithms["PBEWITHSHAAND40BITRC2-CBC"] = "PBEwithSHA-1and40bitRC2-CBC"; + algorithms["PBEWITHSHA1AND40BITRC2-CBC"] = "PBEwithSHA-1and40bitRC2-CBC"; + algorithms["PBEWITHSHA-1AND40BITRC2-CBC"] = "PBEwithSHA-1and40bitRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc.Id] = "PBEwithSHA-1and40bitRC2-CBC"; + algorithms["PBEWITHSHAAND128BITAES-CBC-BC"] = "PBEwithSHA-1and128bitAES-CBC-BC"; + algorithms["PBEWITHSHA1AND128BITAES-CBC-BC"] = "PBEwithSHA-1and128bitAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND128BITAES-CBC-BC"] = "PBEwithSHA-1and128bitAES-CBC-BC"; + algorithms["PBEWITHSHAAND192BITAES-CBC-BC"] = "PBEwithSHA-1and192bitAES-CBC-BC"; + algorithms["PBEWITHSHA1AND192BITAES-CBC-BC"] = "PBEwithSHA-1and192bitAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND192BITAES-CBC-BC"] = "PBEwithSHA-1and192bitAES-CBC-BC"; + algorithms["PBEWITHSHAAND256BITAES-CBC-BC"] = "PBEwithSHA-1and256bitAES-CBC-BC"; + algorithms["PBEWITHSHA1AND256BITAES-CBC-BC"] = "PBEwithSHA-1and256bitAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND256BITAES-CBC-BC"] = "PBEwithSHA-1and256bitAES-CBC-BC"; + algorithms["PBEWITHSHA256AND128BITAES-CBC-BC"] = "PBEwithSHA-256and128bitAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND128BITAES-CBC-BC"] = "PBEwithSHA-256and128bitAES-CBC-BC"; + algorithms["PBEWITHSHA256AND192BITAES-CBC-BC"] = "PBEwithSHA-256and192bitAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND192BITAES-CBC-BC"] = "PBEwithSHA-256and192bitAES-CBC-BC"; + algorithms["PBEWITHSHA256AND256BITAES-CBC-BC"] = "PBEwithSHA-256and256bitAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND256BITAES-CBC-BC"] = "PBEwithSHA-256and256bitAES-CBC-BC"; + algorithms["PBEWITHSHAANDIDEA"] = "PBEwithSHA-1andIDEA-CBC"; + algorithms["PBEWITHSHAANDIDEA-CBC"] = "PBEwithSHA-1andIDEA-CBC"; + algorithms["PBEWITHSHAANDTWOFISH"] = "PBEwithSHA-1andTWOFISH-CBC"; + algorithms["PBEWITHSHAANDTWOFISH-CBC"] = "PBEwithSHA-1andTWOFISH-CBC"; + algorithms["PBEWITHHMACSHA1"] = "PBEwithHmacSHA-1"; + algorithms["PBEWITHHMACSHA-1"] = "PBEwithHmacSHA-1"; + algorithms[OiwObjectIdentifiers.IdSha1.Id] = "PBEwithHmacSHA-1"; + algorithms["PBEWITHHMACSHA224"] = "PBEwithHmacSHA-224"; + algorithms["PBEWITHHMACSHA-224"] = "PBEwithHmacSHA-224"; + algorithms[NistObjectIdentifiers.IdSha224.Id] = "PBEwithHmacSHA-224"; + algorithms["PBEWITHHMACSHA256"] = "PBEwithHmacSHA-256"; + algorithms["PBEWITHHMACSHA-256"] = "PBEwithHmacSHA-256"; + algorithms[NistObjectIdentifiers.IdSha256.Id] = "PBEwithHmacSHA-256"; + algorithms["PBEWITHHMACRIPEMD128"] = "PBEwithHmacRipeMD128"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD128.Id] = "PBEwithHmacRipeMD128"; + algorithms["PBEWITHHMACRIPEMD160"] = "PBEwithHmacRipeMD160"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD160.Id] = "PBEwithHmacRipeMD160"; + algorithms["PBEWITHHMACRIPEMD256"] = "PBEwithHmacRipeMD256"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD256.Id] = "PBEwithHmacRipeMD256"; + algorithms["PBEWITHHMACTIGER"] = "PBEwithHmacTiger"; + + algorithms["PBEWITHMD5AND128BITAES-CBC-OPENSSL"] = "PBEwithMD5and128bitAES-CBC-OpenSSL"; + algorithms["PBEWITHMD5AND192BITAES-CBC-OPENSSL"] = "PBEwithMD5and192bitAES-CBC-OpenSSL"; + algorithms["PBEWITHMD5AND256BITAES-CBC-OPENSSL"] = "PBEwithMD5and256bitAES-CBC-OpenSSL"; + + algorithmType["Pkcs5scheme1"] = Pkcs5S1; + algorithmType["Pkcs5scheme2"] = Pkcs5S2; + algorithmType["PBEwithMD2andDES-CBC"] = Pkcs5S1; + algorithmType["PBEwithMD2andRC2-CBC"] = Pkcs5S1; + algorithmType["PBEwithMD5andDES-CBC"] = Pkcs5S1; + algorithmType["PBEwithMD5andRC2-CBC"] = Pkcs5S1; + algorithmType["PBEwithSHA-1andDES-CBC"] = Pkcs5S1; + algorithmType["PBEwithSHA-1andRC2-CBC"] = Pkcs5S1; + algorithmType["Pkcs12"] = Pkcs12; + algorithmType["PBEwithSHA-1and128bitRC4"] = Pkcs12; + algorithmType["PBEwithSHA-1and40bitRC4"] = Pkcs12; + algorithmType["PBEwithSHA-1and3-keyDESEDE-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1and2-keyDESEDE-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1and128bitRC2-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1and40bitRC2-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1and128bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-1and192bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-1and256bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-256and128bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-256and192bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-256and256bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-1andIDEA-CBC"] = "Pkcs12"; + algorithmType["PBEwithSHA-1andTWOFISH-CBC"] = "Pkcs12"; + algorithmType["PBEwithHmacSHA-1"] = Pkcs12; + algorithmType["PBEwithHmacSHA-224"] = Pkcs12; + algorithmType["PBEwithHmacSHA-256"] = Pkcs12; + algorithmType["PBEwithHmacRipeMD128"] = Pkcs12; + algorithmType["PBEwithHmacRipeMD160"] = Pkcs12; + algorithmType["PBEwithHmacRipeMD256"] = Pkcs12; + algorithmType["PBEwithHmacTiger"] = Pkcs12; + + algorithmType["PBEwithMD5and128bitAES-CBC-OpenSSL"] = OpenSsl; + algorithmType["PBEwithMD5and192bitAES-CBC-OpenSSL"] = OpenSsl; + algorithmType["PBEwithMD5and256bitAES-CBC-OpenSSL"] = OpenSsl; + + oids["PBEwithMD2andDES-CBC"] = PkcsObjectIdentifiers.PbeWithMD2AndDesCbc; + oids["PBEwithMD2andRC2-CBC"] = PkcsObjectIdentifiers.PbeWithMD2AndRC2Cbc; + oids["PBEwithMD5andDES-CBC"] = PkcsObjectIdentifiers.PbeWithMD5AndDesCbc; + oids["PBEwithMD5andRC2-CBC"] = PkcsObjectIdentifiers.PbeWithMD5AndRC2Cbc; + oids["PBEwithSHA-1andDES-CBC"] = PkcsObjectIdentifiers.PbeWithSha1AndDesCbc; + oids["PBEwithSHA-1andRC2-CBC"] = PkcsObjectIdentifiers.PbeWithSha1AndRC2Cbc; + oids["PBEwithSHA-1and128bitRC4"] = PkcsObjectIdentifiers.PbeWithShaAnd128BitRC4; + oids["PBEwithSHA-1and40bitRC4"] = PkcsObjectIdentifiers.PbeWithShaAnd40BitRC4; + oids["PBEwithSHA-1and3-keyDESEDE-CBC"] = PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc; + oids["PBEwithSHA-1and2-keyDESEDE-CBC"] = PkcsObjectIdentifiers.PbeWithShaAnd2KeyTripleDesCbc; + oids["PBEwithSHA-1and128bitRC2-CBC"] = PkcsObjectIdentifiers.PbeWithShaAnd128BitRC2Cbc; + oids["PBEwithSHA-1and40bitRC2-CBC"] = PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc; + oids["PBEwithHmacSHA-1"] = OiwObjectIdentifiers.IdSha1; + oids["PBEwithHmacSHA-224"] = NistObjectIdentifiers.IdSha224; + oids["PBEwithHmacSHA-256"] = NistObjectIdentifiers.IdSha256; + oids["PBEwithHmacRipeMD128"] = TeleTrusTObjectIdentifiers.RipeMD128; + oids["PBEwithHmacRipeMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; + oids["PBEwithHmacRipeMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; + } + + static PbeParametersGenerator MakePbeGenerator( + string type, + IDigest digest, + byte[] key, + byte[] salt, + int iterationCount) + { + PbeParametersGenerator generator; + + if (type.Equals(Pkcs5S1)) + { + generator = new Pkcs5S1ParametersGenerator(digest); + } + else if (type.Equals(Pkcs5S2)) + { + generator = new Pkcs5S2ParametersGenerator(); + } + else if (type.Equals(Pkcs12)) + { + generator = new Pkcs12ParametersGenerator(digest); + } + else if (type.Equals(OpenSsl)) + { + generator = new OpenSslPbeParametersGenerator(); + } + else + { + throw new ArgumentException("Unknown PBE type: " + type, "type"); + } + + generator.Init(key, salt, iterationCount); + return generator; + } + + /// + /// Returns a ObjectIdentifier for a give encoding. + /// + /// A string representation of the encoding. + /// A DerObjectIdentifier, null if the Oid is not available. + public static DerObjectIdentifier GetObjectIdentifier( + string mechanism) + { + mechanism = (string) algorithms[mechanism.ToUpper(CultureInfo.InvariantCulture)]; + if (mechanism != null) + { + return (DerObjectIdentifier)oids[mechanism]; + } + return null; + } + + public static ICollection Algorithms + { + get { return oids.Keys; } + } + + public static bool IsPkcs12( + string algorithm) + { + string mechanism = (string) algorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + + return mechanism != null && Pkcs12.Equals(algorithmType[mechanism]); + } + + public static bool IsPkcs5Scheme1( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + + return mechanism != null && Pkcs5S1.Equals(algorithmType[mechanism]); + } + + public static bool IsPkcs5Scheme2( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + + return mechanism != null && Pkcs5S2.Equals(algorithmType[mechanism]); + } + + public static bool IsOpenSsl( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + + return mechanism != null && OpenSsl.Equals(algorithmType[mechanism]); + } + + public static bool IsPbeAlgorithm( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + + return mechanism != null && algorithmType[mechanism] != null; + } + + public static Asn1Encodable GenerateAlgorithmParameters( + DerObjectIdentifier algorithmOid, + byte[] salt, + int iterationCount) + { + return GenerateAlgorithmParameters(algorithmOid.Id, salt, iterationCount); + } + + public static Asn1Encodable GenerateAlgorithmParameters( + string algorithm, + byte[] salt, + int iterationCount) + { + if (IsPkcs12(algorithm)) + { + return new Pkcs12PbeParams(salt, iterationCount); + } + else if (IsPkcs5Scheme2(algorithm)) + { + return new Pbkdf2Params(salt, iterationCount); + } + else + { + return new PbeParameter(salt, iterationCount); + } + } + + public static ICipherParameters GenerateCipherParameters( + DerObjectIdentifier algorithmOid, + char[] password, + Asn1Encodable pbeParameters) + { + return GenerateCipherParameters(algorithmOid.Id, password, false, pbeParameters); + } + + public static ICipherParameters GenerateCipherParameters( + DerObjectIdentifier algorithmOid, + char[] password, + bool wrongPkcs12Zero, + Asn1Encodable pbeParameters) + { + return GenerateCipherParameters(algorithmOid.Id, password, wrongPkcs12Zero, pbeParameters); + } + + public static ICipherParameters GenerateCipherParameters( + string algorithm, + char[] password, + Asn1Encodable pbeParameters) + { + return GenerateCipherParameters(algorithm, password, false, pbeParameters); + } + + public static ICipherParameters GenerateCipherParameters( + string algorithm, + char[] password, + bool wrongPkcs12Zero, + Asn1Encodable pbeParameters) + { + string mechanism = (string) algorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + byte[] keyBytes; + //string type = (string)algorithmType[mechanism]; + byte[] salt; + int iterationCount; + + if (IsPkcs12(mechanism)) + { + Pkcs12PbeParams pbeParams = Pkcs12PbeParams.GetInstance(pbeParameters); + salt = pbeParams.GetIV(); + iterationCount = pbeParams.Iterations.IntValue; + keyBytes = PbeParametersGenerator.Pkcs12PasswordToBytes(password, wrongPkcs12Zero); + } + else if (IsPkcs5Scheme2(mechanism)) + { + Pbkdf2Params pbeParams = Pbkdf2Params.GetInstance(pbeParameters); + salt = pbeParams.GetSalt(); + iterationCount = pbeParams.IterationCount.IntValue; + keyBytes = PbeParametersGenerator.Pkcs5PasswordToBytes(password); + } + else + { + PbeParameter pbeParams = PbeParameter.GetInstance(pbeParameters); + salt = pbeParams.GetSalt(); + iterationCount = pbeParams.IterationCount.IntValue; + keyBytes = PbeParametersGenerator.Pkcs5PasswordToBytes(password); + } + + ICipherParameters parameters = null; + + if (mechanism.StartsWith("PBEwithSHA-1")) + { + PbeParametersGenerator generator = MakePbeGenerator( + (string) algorithmType[mechanism], new Sha1Digest(), keyBytes, salt, iterationCount); + + if (mechanism.Equals("PBEwithSHA-1and128bitRC4")) + { + parameters = generator.GenerateDerivedParameters("RC4", 128); + } + else if (mechanism.Equals("PBEwithSHA-1and40bitRC4")) + { + parameters = generator.GenerateDerivedParameters("RC4", 40); + } + else if (mechanism.Equals("PBEwithSHA-1and3-keyDESEDE-CBC")) + { + parameters = generator.GenerateDerivedParameters("DESEDE", 192, 64); + } + else if (mechanism.Equals("PBEwithSHA-1and2-keyDESEDE-CBC")) + { + parameters = generator.GenerateDerivedParameters("DESEDE", 128, 64); + } + else if (mechanism.Equals("PBEwithSHA-1and128bitRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 128, 64); + } + else if (mechanism.Equals("PBEwithSHA-1and40bitRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 40, 64); + } + else if (mechanism.Equals("PBEwithSHA-1andDES-CBC")) + { + parameters = generator.GenerateDerivedParameters("DES", 64, 64); + } + else if (mechanism.Equals("PBEwithSHA-1andRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 64, 64); + } + else if (mechanism.Equals("PBEwithSHA-1and128bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 128, 128); + } + else if (mechanism.Equals("PBEwithSHA-1and192bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 192, 128); + } + else if (mechanism.Equals("PBEwithSHA-1and256bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 256, 128); + } + } + else if (mechanism.StartsWith("PBEwithSHA-256")) + { + PbeParametersGenerator generator = MakePbeGenerator( + (string) algorithmType[mechanism], new Sha256Digest(), keyBytes, salt, iterationCount); + + if (mechanism.Equals("PBEwithSHA-256and128bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 128, 128); + } + else if (mechanism.Equals("PBEwithSHA-256and192bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 192, 128); + } + else if (mechanism.Equals("PBEwithSHA-256and256bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 256, 128); + } + } + else if (mechanism.StartsWith("PBEwithMD5")) + { + PbeParametersGenerator generator = MakePbeGenerator( + (string)algorithmType[mechanism], new MD5Digest(), keyBytes, salt, iterationCount); + + if (mechanism.Equals("PBEwithMD5andDES-CBC")) + { + parameters = generator.GenerateDerivedParameters("DES", 64, 64); + } + else if (mechanism.Equals("PBEwithMD5andRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 64, 64); + } + else if (mechanism.Equals("PBEwithMD5and128bitAES-CBC-OpenSSL")) + { + parameters = generator.GenerateDerivedParameters("AES", 128, 128); + } + else if (mechanism.Equals("PBEwithMD5and192bitAES-CBC-OpenSSL")) + { + parameters = generator.GenerateDerivedParameters("AES", 192, 128); + } + else if (mechanism.Equals("PBEwithMD5and256bitAES-CBC-OpenSSL")) + { + parameters = generator.GenerateDerivedParameters("AES", 256, 128); + } + } + else if (mechanism.StartsWith("PBEwithMD2")) + { + PbeParametersGenerator generator = MakePbeGenerator( + (string)algorithmType[mechanism], new MD2Digest(), keyBytes, salt, iterationCount); + if (mechanism.Equals("PBEwithMD2andDES-CBC")) + { + parameters = generator.GenerateDerivedParameters("DES", 64, 64); + } + else if (mechanism.Equals("PBEwithMD2andRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 64, 64); + } + } + else if (mechanism.StartsWith("PBEwithHmac")) + { + string digestName = mechanism.Substring("PBEwithHmac".Length); + IDigest digest = DigestUtilities.GetDigest(digestName); + + PbeParametersGenerator generator = MakePbeGenerator( + (string) algorithmType[mechanism], digest, keyBytes, salt, iterationCount); + + int bitLen = digest.GetDigestSize() * 8; + parameters = generator.GenerateDerivedMacParameters(bitLen); + } + + Array.Clear(keyBytes, 0, keyBytes.Length); + + return parameters; + } + + public static object CreateEngine( + DerObjectIdentifier algorithmOid) + { + return CreateEngine(algorithmOid.Id); + } + + private static bool EndsWith( + string s, + string ending) + { + int pos = s.Length - ending.Length; + + return pos >= 0 && s.Substring(pos) == ending; + } + + public static object CreateEngine( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpper(CultureInfo.InvariantCulture)]; + + if (mechanism.StartsWith("PBEwithHmac")) + { + string digestName = mechanism.Substring("PBEwithHmac".Length); + + return MacUtilities.GetMac("HMAC/" + digestName); + } + + if (mechanism.StartsWith("PBEwithMD2") + || mechanism.StartsWith("PBEwithMD5") + || mechanism.StartsWith("PBEwithSHA-1")) + { + if (EndsWith(mechanism, "RC2-CBC")) + { + return CipherUtilities.GetCipher("RC2/CBC"); + } + + if (EndsWith(mechanism, "RC4")) + { + return CipherUtilities.GetCipher("RC4"); + } + + if (EndsWith(mechanism, "DES-CBC")) + { + return CipherUtilities.GetCipher("DES/CBC"); + } + + if (EndsWith(mechanism, "DESEDE-CBC")) + { + return CipherUtilities.GetCipher("DESEDE/CBC"); + } + } + + return null; + } + + public static string GetEncodingName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + } +} diff --git a/src/core/srcbc/security/PrivateKeyFactory.cs b/src/core/srcbc/security/PrivateKeyFactory.cs new file mode 100644 index 0000000..79fa72d --- /dev/null +++ b/src/core/srcbc/security/PrivateKeyFactory.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Security +{ + public sealed class PrivateKeyFactory + { + private PrivateKeyFactory() + { + } + + public static AsymmetricKeyParameter CreateKey( + byte[] privateKeyInfoData) + { + return CreateKey( + PrivateKeyInfo.GetInstance( + Asn1Object.FromByteArray(privateKeyInfoData))); + } + + public static AsymmetricKeyParameter CreateKey( + Stream inStr) + { + return CreateKey( + PrivateKeyInfo.GetInstance( + Asn1Object.FromStream(inStr))); + } + + public static AsymmetricKeyParameter CreateKey( + PrivateKeyInfo keyInfo) + { + AlgorithmIdentifier algID = keyInfo.AlgorithmID; + DerObjectIdentifier algOid = algID.ObjectID; + + if (algOid.Equals(PkcsObjectIdentifiers.RsaEncryption)) + { + RsaPrivateKeyStructure keyStructure = new RsaPrivateKeyStructure( + Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + + return new RsaPrivateCrtKeyParameters( + keyStructure.Modulus, + keyStructure.PublicExponent, + keyStructure.PrivateExponent, + keyStructure.Prime1, + keyStructure.Prime2, + keyStructure.Exponent1, + keyStructure.Exponent2, + keyStructure.Coefficient); + } + else if (algOid.Equals(PkcsObjectIdentifiers.DhKeyAgreement)) + { + DHParameter para = new DHParameter( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + DerInteger derX = (DerInteger)keyInfo.PrivateKey; + + return new DHPrivateKeyParameters( + derX.Value, + new DHParameters(para.P, para.G)); + } + else if (algOid.Equals(OiwObjectIdentifiers.ElGamalAlgorithm)) + { + ElGamalParameter para = new ElGamalParameter( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + DerInteger derX = (DerInteger)keyInfo.PrivateKey; + + return new ElGamalPrivateKeyParameters( + derX.Value, + new ElGamalParameters(para.P, para.G)); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdDsa)) + { + DerInteger derX = (DerInteger) keyInfo.PrivateKey; + Asn1Encodable ae = algID.Parameters; + + DsaParameters parameters = null; + if (ae != null) + { + DsaParameter para = DsaParameter.GetInstance(ae.ToAsn1Object()); + parameters = new DsaParameters(para.P, para.Q, para.G); + } + + return new DsaPrivateKeyParameters(derX.Value, parameters); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdECPublicKey)) + { + X962Parameters para = new X962Parameters(algID.Parameters.ToAsn1Object()); + X9ECParameters ecP; + + if (para.IsNamedCurve) + { + // TODO ECGost3410NamedCurves support (returns ECDomainParameters though) + + DerObjectIdentifier oid = (DerObjectIdentifier) para.Parameters; + ecP = X962NamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = SecNamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = NistNamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.GetByOid(oid); + } + } + } + } + else + { + ecP = new X9ECParameters((Asn1Sequence) para.Parameters); + } + + ECDomainParameters dParams = new ECDomainParameters( + ecP.Curve, + ecP.G, + ecP.N, + ecP.H, + ecP.GetSeed()); + + ECPrivateKeyStructure ec = new ECPrivateKeyStructure( + Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + + return new ECPrivateKeyParameters(ec.GetKey(), dParams); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x2001)) + { + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + + ECPrivateKeyStructure ec = new ECPrivateKeyStructure( + Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + + ECDomainParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet); + + if (ecP == null) + return null; + + return new ECPrivateKeyParameters(ec.GetKey(), gostParams.PublicKeyParamSet); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x94)) + { + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + + DerOctetString derX = (DerOctetString) keyInfo.PrivateKey; + byte[] keyEnc = derX.GetOctets(); + byte[] keyBytes = new byte[keyEnc.Length]; + + for (int i = 0; i != keyEnc.Length; i++) + { + keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // was little endian + } + + BigInteger x = new BigInteger(1, keyBytes); + + return new Gost3410PrivateKeyParameters(x, gostParams.PublicKeyParamSet); + } + else + { + throw new SecurityUtilityException("algorithm identifier in key not recognised"); + } + } + } +} diff --git a/src/core/srcbc/security/PublicKeyFactory.cs b/src/core/srcbc/security/PublicKeyFactory.cs new file mode 100644 index 0000000..775b84e --- /dev/null +++ b/src/core/srcbc/security/PublicKeyFactory.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Security +{ + public sealed class PublicKeyFactory + { + private PublicKeyFactory() + { + } + + public static AsymmetricKeyParameter CreateKey( + byte[] keyInfoData) + { + return CreateKey( + SubjectPublicKeyInfo.GetInstance( + Asn1Object.FromByteArray(keyInfoData))); + } + + public static AsymmetricKeyParameter CreateKey( + Stream inStr) + { + return CreateKey( + SubjectPublicKeyInfo.GetInstance( + Asn1Object.FromStream(inStr))); + } + + public static AsymmetricKeyParameter CreateKey( + SubjectPublicKeyInfo keyInfo) + { + AlgorithmIdentifier algID = keyInfo.AlgorithmID; + DerObjectIdentifier algOid = algID.ObjectID; + + if (algOid.Equals(PkcsObjectIdentifiers.RsaEncryption) + || algOid.Equals(X509ObjectIdentifiers.IdEARsa)) + { + RsaPublicKeyStructure pubKey = RsaPublicKeyStructure.GetInstance( + keyInfo.GetPublicKey()); + + return new RsaKeyParameters(false, pubKey.Modulus, pubKey.PublicExponent); + } + else if (algOid.Equals(PkcsObjectIdentifiers.DhKeyAgreement) + || algOid.Equals(X9ObjectIdentifiers.DHPublicNumber)) + { + DHParameter para = new DHParameter( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + DerInteger derY = (DerInteger) keyInfo.GetPublicKey(); + + return new DHPublicKeyParameters(derY.Value, new DHParameters(para.P, para.G)); + } + else if (algOid.Equals(OiwObjectIdentifiers.ElGamalAlgorithm)) + { + ElGamalParameter para = new ElGamalParameter( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + DerInteger derY = (DerInteger) keyInfo.GetPublicKey(); + + return new ElGamalPublicKeyParameters( + derY.Value, + new ElGamalParameters(para.P, para.G)); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdDsa) + || algOid.Equals(OiwObjectIdentifiers.DsaWithSha1)) + { + DerInteger derY = (DerInteger) keyInfo.GetPublicKey(); + Asn1Encodable ae = algID.Parameters; + + DsaParameters parameters = null; + if (ae != null) + { + DsaParameter para = DsaParameter.GetInstance(ae.ToAsn1Object()); + parameters = new DsaParameters(para.P, para.Q, para.G); + } + + return new DsaPublicKeyParameters(derY.Value, parameters); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdECPublicKey)) + { + X962Parameters para = new X962Parameters( + algID.Parameters.ToAsn1Object()); + X9ECParameters ecP; + + if (para.IsNamedCurve) + { + // TODO ECGost3410NamedCurves support (returns ECDomainParameters though) + + DerObjectIdentifier oid = (DerObjectIdentifier)para.Parameters; + ecP = X962NamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = SecNamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = NistNamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.GetByOid(oid); + } + } + } + } + else + { + ecP = new X9ECParameters((Asn1Sequence)para.Parameters.ToAsn1Object()); + } + + ECDomainParameters dParams = new ECDomainParameters( + ecP.Curve, + ecP.G, + ecP.N, + ecP.H, + ecP.GetSeed()); + + DerBitString bits = keyInfo.PublicKeyData; + byte[] data = bits.GetBytes(); + Asn1OctetString key = new DerOctetString(data); + + X9ECPoint derQ = new X9ECPoint(dParams.Curve, key); + + return new ECPublicKeyParameters(derQ.Point, dParams); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x2001)) + { + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + (Asn1Sequence) algID.Parameters); + + Asn1OctetString key; + try + { + key = (Asn1OctetString) keyInfo.GetPublicKey(); + } + catch (IOException) + { + throw new ArgumentException("invalid info structure in GOST3410 public key"); + } + + byte[] keyEnc = key.GetOctets(); + byte[] x = new byte[32]; + byte[] y = new byte[32]; + + for (int i = 0; i != y.Length; i++) + { + x[i] = keyEnc[32 - 1 - i]; + } + + for (int i = 0; i != x.Length; i++) + { + y[i] = keyEnc[64 - 1 - i]; + } + + ECDomainParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet); + + if (ecP == null) + return null; + + ECPoint q = ecP.Curve.CreatePoint(new BigInteger(1, x), new BigInteger(1, y), false); + + return new ECPublicKeyParameters(q, gostParams.PublicKeyParamSet); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x94)) + { + Gost3410PublicKeyAlgParameters algParams = new Gost3410PublicKeyAlgParameters( + (Asn1Sequence) algID.Parameters); + + DerOctetString derY; + try + { + derY = (DerOctetString) keyInfo.GetPublicKey(); + } + catch (IOException) + { + throw new ArgumentException("invalid info structure in GOST3410 public key"); + } + + byte[] keyEnc = derY.GetOctets(); + byte[] keyBytes = new byte[keyEnc.Length]; + + for (int i = 0; i != keyEnc.Length; i++) + { + keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // was little endian + } + + BigInteger y = new BigInteger(1, keyBytes); + + return new Gost3410PublicKeyParameters(y, algParams.PublicKeyParamSet); + } + else + { + throw new SecurityUtilityException("algorithm identifier in key not recognised: " + algOid); + } + } + } +} diff --git a/src/core/srcbc/security/SecureRandom.cs b/src/core/srcbc/security/SecureRandom.cs new file mode 100644 index 0000000..bffb5a9 --- /dev/null +++ b/src/core/srcbc/security/SecureRandom.cs @@ -0,0 +1,224 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Prng; + +namespace Org.BouncyCastle.Security +{ + public class SecureRandom + : Random + { + private static readonly SecureRandom[] master = { null }; + private static SecureRandom Master + { + get + { + if (master[0] == null) + { + IRandomGenerator gen = new DigestRandomGenerator(new Sha256Digest()); + gen = new ReversedWindowGenerator(gen, 32); + SecureRandom sr = master[0] = new SecureRandom(gen); + + sr.SetSeed(DateTime.Now.Ticks); + sr.SetSeed(new ThreadedSeedGenerator().GenerateSeed(24, true)); + sr.GenerateSeed(1 + sr.Next(32)); + } + + return master[0]; + } + } + + public static SecureRandom GetInstance( + string algorithm) + { + // TODO Compared to JDK, we don't auto-seed if the client forgets - problem? + + // TODO Support all digests more generally, by stripping PRNG and calling DigestUtilities? + IDigest digest = null; + switch (algorithm.ToUpper(CultureInfo.InvariantCulture)) + { + case "SHA1PRNG": + digest = new Sha1Digest(); + break; + case "SHA256PRNG": + digest = new Sha256Digest(); + break; + } + + if (digest != null) + { + return new SecureRandom(new DigestRandomGenerator(digest)); + } + + throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm"); + } + + public static byte[] GetSeed( + int length) + { + return Master.GenerateSeed(length); + } + + protected IRandomGenerator generator; + + public SecureRandom() + : base(0) + { + this.generator = new DigestRandomGenerator(new Sha1Digest()); + SetSeed(GetSeed(8)); + } + + public SecureRandom( + byte[] inSeed) + : base(0) + { + this.generator = new DigestRandomGenerator(new Sha1Digest()); + SetSeed(inSeed); + } + + /// Use the specified instance of IRandomGenerator as random source. + /// + /// This constructor performs no seeding of either the IRandomGenerator or the + /// constructed SecureRandom. It is the responsibility of the client to provide + /// proper seed material as necessary/appropriate for the given IRandomGenerator + /// implementation. + /// + /// The source to generate all random bytes from. + public SecureRandom( + IRandomGenerator generator) + : base(0) + { + this.generator = generator; + } + + public virtual byte[] GenerateSeed( + int length) + { + SetSeed(DateTime.Now.Ticks); + + byte[] rv = new byte[length]; + NextBytes(rv); + return rv; + } + + public virtual void SetSeed( + byte[] inSeed) + { + generator.AddSeedMaterial(inSeed); + } + + public virtual void SetSeed( + long seed) + { + generator.AddSeedMaterial(seed); + } + + public override int Next() + { + for (;;) + { + int i = NextInt() & int.MaxValue; + + if (i != int.MaxValue) + return i; + } + } + + public override int Next( + int maxValue) + { + if (maxValue < 2) + { + if (maxValue < 0) + throw new ArgumentOutOfRangeException("maxValue < 0"); + + return 0; + } + + // Test whether maxValue is a power of 2 + if ((maxValue & -maxValue) == maxValue) + { + int val = NextInt() & int.MaxValue; + long lr = ((long) maxValue * (long) val) >> 31; + return (int) lr; + } + + int bits, result; + do + { + bits = NextInt() & int.MaxValue; + result = bits % maxValue; + } + while (bits - result + (maxValue - 1) < 0); // Ignore results near overflow + + return result; + } + + public override int Next( + int minValue, + int maxValue) + { + if (maxValue <= minValue) + { + if (maxValue == minValue) + return minValue; + + throw new ArgumentException("maxValue cannot be less than minValue"); + } + + int diff = maxValue - minValue; + if (diff > 0) + return minValue + Next(diff); + + for (;;) + { + int i = NextInt(); + + if (i >= minValue && i < maxValue) + return i; + } + } + + public override void NextBytes( + byte[] buffer) + { + generator.NextBytes(buffer); + } + + public virtual void NextBytes( + byte[] buffer, + int start, + int length) + { + generator.NextBytes(buffer, start, length); + } + + private static readonly double DoubleScale = System.Math.Pow(2.0, 64.0); + + public override double NextDouble() + { + return Convert.ToDouble((ulong) NextLong()) / DoubleScale; + } + + public virtual int NextInt() + { + byte[] intBytes = new byte[4]; + NextBytes(intBytes); + + int result = 0; + for (int i = 0; i < 4; i++) + { + result = (result << 8) + (intBytes[i] & 0xff); + } + + return result; + } + + public virtual long NextLong() + { + return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt(); + } + } +} diff --git a/src/core/srcbc/security/SecurityUtilityException.cs b/src/core/srcbc/security/SecurityUtilityException.cs new file mode 100644 index 0000000..e57a00a --- /dev/null +++ b/src/core/srcbc/security/SecurityUtilityException.cs @@ -0,0 +1,33 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class SecurityUtilityException + : Exception + { + /** + * base constructor. + */ + public SecurityUtilityException() + { + } + + /** + * create a SecurityUtilityException with the given message. + * + * @param message the message to be carried with the exception. + */ + public SecurityUtilityException( + string message) + : base(message) + { + } + + public SecurityUtilityException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/src/core/srcbc/security/SignatureException.cs b/src/core/srcbc/security/SignatureException.cs new file mode 100644 index 0000000..a1a631b --- /dev/null +++ b/src/core/srcbc/security/SignatureException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class SignatureException : GeneralSecurityException + { + public SignatureException() : base() { } + public SignatureException(string message) : base(message) { } + public SignatureException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/security/SignerUtilities.cs b/src/core/srcbc/security/SignerUtilities.cs new file mode 100644 index 0000000..e684aef --- /dev/null +++ b/src/core/srcbc/security/SignerUtilities.cs @@ -0,0 +1,517 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Signers; + +namespace Org.BouncyCastle.Security +{ + /// + /// Signer Utility class contains methods that can not be specifically grouped into other classes. + /// + public sealed class SignerUtilities + { + private SignerUtilities() + { + } + + internal static readonly Hashtable algorithms = new Hashtable(); + internal static readonly Hashtable oids = new Hashtable(); + + static SignerUtilities() + { + algorithms["MD2WITHRSA"] = "MD2withRSA"; + algorithms["MD2WITHRSAENCRYPTION"] = "MD2withRSA"; + algorithms[PkcsObjectIdentifiers.MD2WithRsaEncryption.Id] = "MD2withRSA"; + + algorithms["MD4WITHRSA"] = "MD4withRSA"; + algorithms["MD4WITHRSAENCRYPTION"] = "MD4withRSA"; + algorithms[PkcsObjectIdentifiers.MD4WithRsaEncryption.Id] = "MD4withRSA"; + + algorithms["MD5WITHRSA"] = "MD5withRSA"; + algorithms["MD5WITHRSAENCRYPTION"] = "MD5withRSA"; + algorithms[PkcsObjectIdentifiers.MD5WithRsaEncryption.Id] = "MD5withRSA"; + + algorithms["SHA1WITHRSA"] = "SHA-1withRSA"; + algorithms["SHA1WITHRSAENCRYPTION"] = "SHA-1withRSA"; + algorithms[PkcsObjectIdentifiers.Sha1WithRsaEncryption.Id] = "SHA-1withRSA"; + algorithms["SHA-1WITHRSA"] = "SHA-1withRSA"; + + algorithms["SHA224WITHRSA"] = "SHA-224withRSA"; + algorithms["SHA224WITHRSAENCRYPTION"] = "SHA-224withRSA"; + algorithms[PkcsObjectIdentifiers.Sha224WithRsaEncryption.Id] = "SHA-224withRSA"; + algorithms["SHA-224WITHRSA"] = "SHA-224withRSA"; + + algorithms["SHA256WITHRSA"] = "SHA-256withRSA"; + algorithms["SHA256WITHRSAENCRYPTION"] = "SHA-256withRSA"; + algorithms[PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id] = "SHA-256withRSA"; + algorithms["SHA-256WITHRSA"] = "SHA-256withRSA"; + + algorithms["SHA384WITHRSA"] = "SHA-384withRSA"; + algorithms["SHA384WITHRSAENCRYPTION"] = "SHA-384withRSA"; + algorithms[PkcsObjectIdentifiers.Sha384WithRsaEncryption.Id] = "SHA-384withRSA"; + algorithms["SHA-384WITHRSA"] = "SHA-384withRSA"; + + algorithms["SHA512WITHRSA"] = "SHA-512withRSA"; + algorithms["SHA512WITHRSAENCRYPTION"] = "SHA-512withRSA"; + algorithms[PkcsObjectIdentifiers.Sha512WithRsaEncryption.Id] = "SHA-512withRSA"; + algorithms["SHA-512WITHRSA"] = "SHA-512withRSA"; + + algorithms["PSSWITHRSA"] = "PSSwithRSA"; + algorithms["RSASSA-PSS"] = "PSSwithRSA"; + algorithms[PkcsObjectIdentifiers.IdRsassaPss.Id] = "PSSwithRSA"; + + algorithms["SHA1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1"; + algorithms["SHA-1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1"; + algorithms["SHA1WITHRSA/PSS"] = "SHA-1withRSAandMGF1"; + algorithms["SHA-1WITHRSA/PSS"] = "SHA-1withRSAandMGF1"; + + algorithms["SHA224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1"; + algorithms["SHA-224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1"; + algorithms["SHA224WITHRSA/PSS"] = "SHA-224withRSAandMGF1"; + algorithms["SHA-224WITHRSA/PSS"] = "SHA-224withRSAandMGF1"; + + algorithms["SHA256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; + algorithms["SHA-256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; + algorithms["SHA256WITHRSA/PSS"] = "SHA-256withRSAandMGF1"; + algorithms["SHA-256WITHRSA/PSS"] = "SHA-256withRSAandMGF1"; + + algorithms["SHA384WITHRSAANDMGF1"] = "SHA-384withRSAandMGF1"; + algorithms["SHA-384WITHRSAANDMGF1"] = "SHA-384withRSAandMGF1"; + algorithms["SHA384WITHRSA/PSS"] = "SHA-384withRSAandMGF1"; + algorithms["SHA-384WITHRSA/PSS"] = "SHA-384withRSAandMGF1"; + + algorithms["SHA512WITHRSAANDMGF1"] = "SHA-512withRSAandMGF1"; + algorithms["SHA-512WITHRSAANDMGF1"] = "SHA-512withRSAandMGF1"; + algorithms["SHA512WITHRSA/PSS"] = "SHA-512withRSAandMGF1"; + algorithms["SHA-512WITHRSA/PSS"] = "SHA-512withRSAandMGF1"; + + algorithms["RIPEMD128WITHRSA"] = "RIPEMD128withRSA"; + algorithms["RIPEMD128WITHRSAENCRYPTION"] = "RIPEMD128withRSA"; + algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128.Id] = "RIPEMD128withRSA"; + + algorithms["RIPEMD160WITHRSA"] = "RIPEMD160withRSA"; + algorithms["RIPEMD160WITHRSAENCRYPTION"] = "RIPEMD160withRSA"; + algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160.Id] = "RIPEMD160withRSA"; + + algorithms["RIPEMD256WITHRSA"] = "RIPEMD256withRSA"; + algorithms["RIPEMD256WITHRSAENCRYPTION"] = "RIPEMD256withRSA"; + algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256.Id] = "RIPEMD256withRSA"; + + algorithms["NONEWITHDSA"] = "NONEwithDSA"; + algorithms["DSAWITHNONE"] = "NONEwithDSA"; + + algorithms["DSA"] = "SHA-1withDSA"; + algorithms["DSAWITHSHA1"] = "SHA-1withDSA"; + algorithms["DSAWITHSHA-1"] = "SHA-1withDSA"; + algorithms["SHA/DSA"] = "SHA-1withDSA"; + algorithms["SHA1/DSA"] = "SHA-1withDSA"; + algorithms["SHA-1/DSA"] = "SHA-1withDSA"; + algorithms["SHA1WITHDSA"] = "SHA-1withDSA"; + algorithms["SHA-1WITHDSA"] = "SHA-1withDSA"; + algorithms[X9ObjectIdentifiers.IdDsaWithSha1.Id] = "SHA-1withDSA"; + + algorithms["DSAWITHSHA224"] = "SHA-224withDSA"; + algorithms["DSAWITHSHA-224"] = "SHA-224withDSA"; + algorithms["SHA224/DSA"] = "SHA-224withDSA"; + algorithms["SHA-224/DSA"] = "SHA-224withDSA"; + algorithms["SHA224WITHDSA"] = "SHA-224withDSA"; + algorithms["SHA-224WITHDSA"] = "SHA-224withDSA"; + algorithms[NistObjectIdentifiers.DsaWithSha224.Id] = "SHA-224withDSA"; + + algorithms["DSAWITHSHA256"] = "SHA-256withDSA"; + algorithms["DSAWITHSHA-256"] = "SHA-256withDSA"; + algorithms["SHA256/DSA"] = "SHA-256withDSA"; + algorithms["SHA-256/DSA"] = "SHA-256withDSA"; + algorithms["SHA256WITHDSA"] = "SHA-256withDSA"; + algorithms["SHA-256WITHDSA"] = "SHA-256withDSA"; + algorithms[NistObjectIdentifiers.DsaWithSha256.Id] = "SHA-256withDSA"; + + algorithms["DSAWITHSHA384"] = "SHA-384withDSA"; + algorithms["DSAWITHSHA-384"] = "SHA-384withDSA"; + algorithms["SHA384/DSA"] = "SHA-384withDSA"; + algorithms["SHA-384/DSA"] = "SHA-384withDSA"; + algorithms["SHA384WITHDSA"] = "SHA-384withDSA"; + algorithms["SHA-384WITHDSA"] = "SHA-384withDSA"; + algorithms[NistObjectIdentifiers.DsaWithSha384.Id] = "SHA-384withDSA"; + + algorithms["DSAWITHSHA512"] = "SHA-512withDSA"; + algorithms["DSAWITHSHA-512"] = "SHA-512withDSA"; + algorithms["SHA512/DSA"] = "SHA-512withDSA"; + algorithms["SHA-512/DSA"] = "SHA-512withDSA"; + algorithms["SHA512WITHDSA"] = "SHA-512withDSA"; + algorithms["SHA-512WITHDSA"] = "SHA-512withDSA"; + algorithms[NistObjectIdentifiers.DsaWithSha512.Id] = "SHA-512withDSA"; + + + algorithms["ECDSA"] = "SHA-1withECDSA"; + algorithms["SHA1/ECDSA"] = "SHA-1withECDSA"; + algorithms["SHA-1/ECDSA"] = "SHA-1withECDSA"; + algorithms["ECDSAWITHSHA1"] = "SHA-1withECDSA"; + algorithms["ECDSAWITHSHA-1"] = "SHA-1withECDSA"; + algorithms["SHA1WITHECDSA"] = "SHA-1withECDSA"; + algorithms["SHA-1WITHECDSA"] = "SHA-1withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha1.Id] = "SHA-1withECDSA"; + algorithms[TeleTrusTObjectIdentifiers.ECSignWithSha1.Id] = "SHA-1withECDSA"; + + algorithms["SHA224/ECDSA"] = "SHA-224withECDSA"; + algorithms["SHA-224/ECDSA"] = "SHA-224withECDSA"; + algorithms["ECDSAWITHSHA224"] = "SHA-224withECDSA"; + algorithms["ECDSAWITHSHA-224"] = "SHA-224withECDSA"; + algorithms["SHA224WITHECDSA"] = "SHA-224withECDSA"; + algorithms["SHA-224WITHECDSA"] = "SHA-224withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha224.Id] = "SHA-224withECDSA"; + + algorithms["SHA256/ECDSA"] = "SHA-256withECDSA"; + algorithms["SHA-256/ECDSA"] = "SHA-256withECDSA"; + algorithms["ECDSAWITHSHA256"] = "SHA-256withECDSA"; + algorithms["ECDSAWITHSHA-256"] = "SHA-256withECDSA"; + algorithms["SHA256WITHECDSA"] = "SHA-256withECDSA"; + algorithms["SHA-256WITHECDSA"] = "SHA-256withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha256.Id] = "SHA-256withECDSA"; + + algorithms["SHA384/ECDSA"] = "SHA-384withECDSA"; + algorithms["SHA-384/ECDSA"] = "SHA-384withECDSA"; + algorithms["ECDSAWITHSHA384"] = "SHA-384withECDSA"; + algorithms["ECDSAWITHSHA-384"] = "SHA-384withECDSA"; + algorithms["SHA384WITHECDSA"] = "SHA-384withECDSA"; + algorithms["SHA-384WITHECDSA"] = "SHA-384withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha384.Id] = "SHA-384withECDSA"; + + algorithms["SHA512/ECDSA"] = "SHA-512withECDSA"; + algorithms["SHA-512/ECDSA"] = "SHA-512withECDSA"; + algorithms["ECDSAWITHSHA512"] = "SHA-512withECDSA"; + algorithms["ECDSAWITHSHA-512"] = "SHA-512withECDSA"; + algorithms["SHA512WITHECDSA"] = "SHA-512withECDSA"; + algorithms["SHA-512WITHECDSA"] = "SHA-512withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha512.Id] = "SHA-512withECDSA"; + + algorithms["RIPEMD160/ECDSA"] = "RIPEMD160withECDSA"; + algorithms["SHA-512/ECDSA"] = "RIPEMD160withECDSA"; + algorithms["ECDSAWITHRIPEMD160"] = "RIPEMD160withECDSA"; + algorithms["ECDSAWITHRIPEMD160"] = "RIPEMD160withECDSA"; + algorithms["RIPEMD160WITHECDSA"] = "RIPEMD160withECDSA"; + algorithms["RIPEMD160WITHECDSA"] = "RIPEMD160withECDSA"; + algorithms[TeleTrusTObjectIdentifiers.ECSignWithRipeMD160.Id] = "RIPEMD160withECDSA"; + + algorithms["GOST-3410"] = "GOST3410"; + algorithms["GOST-3410-94"] = "GOST3410"; + algorithms["GOST3411WITHGOST3410"] = "GOST3410"; + algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94.Id] = "GOST3410"; + + algorithms["ECGOST-3410"] = "ECGOST3410"; + algorithms["ECGOST-3410-2001"] = "ECGOST3410"; + algorithms["GOST3411WITHECGOST3410"] = "ECGOST3410"; + algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001.Id] = "ECGOST3410"; + + + + oids["MD2withRSA"] = PkcsObjectIdentifiers.MD2WithRsaEncryption; + oids["MD4withRSA"] = PkcsObjectIdentifiers.MD4WithRsaEncryption; + oids["MD5withRSA"] = PkcsObjectIdentifiers.MD5WithRsaEncryption; + + oids["SHA-1withRSA"] = PkcsObjectIdentifiers.Sha1WithRsaEncryption; + oids["SHA-224withRSA"] = PkcsObjectIdentifiers.Sha224WithRsaEncryption; + oids["SHA-256withRSA"] = PkcsObjectIdentifiers.Sha256WithRsaEncryption; + oids["SHA-384withRSA"] = PkcsObjectIdentifiers.Sha384WithRsaEncryption; + oids["SHA-512withRSA"] = PkcsObjectIdentifiers.Sha512WithRsaEncryption; + + oids["PSSwithRSA"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-1withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-224withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-256withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-384withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-512withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + + oids["RIPEMD128withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128; + oids["RIPEMD160withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160; + oids["RIPEMD256withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256; + + oids["SHA-1withDSA"] = X9ObjectIdentifiers.IdDsaWithSha1; + + oids["SHA-1withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha1; + oids["SHA-224withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha224; + oids["SHA-256withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha256; + oids["SHA-384withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha384; + oids["SHA-512withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha512; + + oids["GOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94; + oids["ECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001; + } + + /// + /// Returns a ObjectIdentifier for a give encoding. + /// + /// A string representation of the encoding. + /// A DerObjectIdentifier, null if the Oid is not available. + // TODO Don't really want to support this + public static DerObjectIdentifier GetObjectIdentifier( + string mechanism) + { + if (mechanism == null) + throw new ArgumentNullException("mechanism"); + + mechanism = mechanism.ToUpper(CultureInfo.InvariantCulture); + string aliased = (string) algorithms[mechanism]; + + if (aliased != null) + mechanism = aliased; + + return (DerObjectIdentifier) oids[mechanism]; + } + + public static ICollection Algorithms + { + get { return oids.Keys; } + } + + public static ISigner GetSigner( + DerObjectIdentifier id) + { + return GetSigner(id.Id); + } + + public static ISigner GetSigner( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + algorithm = algorithm.ToUpper(CultureInfo.InvariantCulture); + + string mechanism = (string) algorithms[algorithm]; + + if (mechanism == null) + mechanism = algorithm; + + if (mechanism.Equals("MD2withRSA")) + { + return (new RsaDigestSigner(new MD2Digest())); + } + if (mechanism.Equals("MD4withRSA")) + { + return (new RsaDigestSigner(new MD4Digest())); + } + if (mechanism.Equals("MD5withRSA")) + { + return (new RsaDigestSigner(new MD5Digest())); + } + if (mechanism.Equals("SHA-1withRSA")) + { + return (new RsaDigestSigner(new Sha1Digest())); + } + if (mechanism.Equals("SHA-224withRSA")) + { + return (new RsaDigestSigner(new Sha224Digest())); + } + if (mechanism.Equals("SHA-256withRSA")) + { + return (new RsaDigestSigner(new Sha256Digest())); + } + if (mechanism.Equals("SHA-384withRSA")) + { + return (new RsaDigestSigner(new Sha384Digest())); + } + if (mechanism.Equals("SHA-512withRSA")) + { + return (new RsaDigestSigner(new Sha512Digest())); + } + if (mechanism.Equals("RIPEMD128withRSA")) + { + return (new RsaDigestSigner(new RipeMD128Digest())); + } + if (mechanism.Equals("RIPEMD160withRSA")) + { + return (new RsaDigestSigner(new RipeMD160Digest())); + } + if (mechanism.Equals("RIPEMD256withRSA")) + { + return (new RsaDigestSigner(new RipeMD256Digest())); + } + + if (mechanism.Equals("PSSwithRSA")) + { + // TODO The Sha1Digest here is a default. In JCE version, the actual digest + // to be used can be overridden by subsequent parameter settings. + return (new PssSigner(new RsaBlindedEngine(), new Sha1Digest())); + } + if (mechanism.Equals("SHA-1withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha1Digest())); + } + if (mechanism.Equals("SHA-224withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha224Digest())); + } + if (mechanism.Equals("SHA-256withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha256Digest())); + } + if (mechanism.Equals("SHA-384withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha384Digest())); + } + if (mechanism.Equals("SHA-512withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha512Digest())); + } + + if (mechanism.Equals("NONEwithDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new NullDigest())); + } + if (mechanism.Equals("SHA-1withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha1Digest())); + } + if (mechanism.Equals("SHA-224withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha224Digest())); + } + if (mechanism.Equals("SHA-256withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha256Digest())); + } + if (mechanism.Equals("SHA-384withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha384Digest())); + } + if (mechanism.Equals("SHA-512withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha512Digest())); + } + + if (mechanism.Equals("SHA-1withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha1Digest())); + } + if (mechanism.Equals("SHA-224withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha224Digest())); + } + if (mechanism.Equals("SHA-256withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha256Digest())); + } + if (mechanism.Equals("SHA-384withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha384Digest())); + } + if (mechanism.Equals("SHA-512withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha512Digest())); + } + + if (mechanism.Equals("RIPEMD160withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new RipeMD160Digest())); + } + + if (mechanism.Equals("SHA1WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha1Digest())); + } + if (mechanism.Equals("SHA224WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha224Digest())); + } + if (mechanism.Equals("SHA256WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha256Digest())); + } + if (mechanism.Equals("SHA384WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha384Digest())); + } + if (mechanism.Equals("SHA512WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha512Digest())); + } + + if (mechanism.Equals("GOST3410")) + { + return new Gost3410DigestSigner(new Gost3410Signer(), new Gost3411Digest()); + } + if (mechanism.Equals("ECGOST3410")) + { + return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411Digest()); + } + + if (mechanism.Equals("SHA1WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new Sha1Digest(), true); + } + if (mechanism.Equals("MD5WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new MD5Digest(), true); + } + if (mechanism.Equals("RIPEMD160WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new RipeMD160Digest(), true); + } + + throw new SecurityUtilityException("Signer " + algorithm + " not recognised."); + } + + public static string GetEncodingName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + private class NullDigest : IDigest + { + private readonly MemoryStream bOut = new MemoryStream(); + + public string AlgorithmName + { + get { return "NULL"; } + } + + public int GetByteLength() + { + // TODO Is this okay? + return 0; + } + + public int GetDigestSize() + { + return (int) bOut.Length; + } + + public void Update(byte b) + { + bOut.WriteByte(b); + } + + public void BlockUpdate(byte[] inBytes, int inOff, int len) + { + bOut.Write(inBytes, inOff, len); + } + + public int DoFinal(byte[] outBytes, int outOff) + { + byte[] res = bOut.ToArray(); + res.CopyTo(outBytes, outOff); + return res.Length; + } + + public void Reset() + { + bOut.SetLength(0); + } + } + + } +} diff --git a/src/core/srcbc/security/WrapperUtilities.cs b/src/core/srcbc/security/WrapperUtilities.cs new file mode 100644 index 0000000..47a2cbc --- /dev/null +++ b/src/core/srcbc/security/WrapperUtilities.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; + +namespace Org.BouncyCastle.Security +{ + /// + /// Utility class for creating IWrapper objects from their names/Oids + /// + public sealed class WrapperUtilities + { + private WrapperUtilities() + { + } + + private static readonly Hashtable algorithms = new Hashtable(); +// private static readonly Hashtable oids = new Hashtable(); + + static WrapperUtilities() + { + algorithms[NistObjectIdentifiers.IdAes128Wrap.Id] = "AESWRAP"; + algorithms[NistObjectIdentifiers.IdAes192Wrap.Id] = "AESWRAP"; + algorithms[NistObjectIdentifiers.IdAes256Wrap.Id] = "AESWRAP"; + + algorithms[NttObjectIdentifiers.IdCamellia128Wrap.Id] = "CAMELLIAWRAP"; + algorithms[NttObjectIdentifiers.IdCamellia192Wrap.Id] = "CAMELLIAWRAP"; + algorithms[NttObjectIdentifiers.IdCamellia256Wrap.Id] = "CAMELLIAWRAP"; + + algorithms[PkcsObjectIdentifiers.IdAlgCms3DesWrap.Id] = "DESEDEWRAP"; + + algorithms[PkcsObjectIdentifiers.IdAlgCmsRC2Wrap.Id] = "RC2WRAP"; + + algorithms[KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap.Id] = "SEEDWRAP"; + } + + public static IWrapper GetWrapper( + DerObjectIdentifier oid) + { + return GetWrapper(oid.Id); + } + + public static IWrapper GetWrapper( + string algorithm) + { + string upper = algorithm.ToUpper(CultureInfo.InvariantCulture); + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + switch (mechanism) + { + case "AESWRAP": + return new AesWrapEngine(); + case "CAMELLIAWRAP": + return new CamelliaWrapEngine(); + case "DESEDEWRAP": + return new DesEdeWrapEngine(); + case "RC2WRAP": + return new RC2WrapEngine(); + case "SEEDWRAP": + return new SeedWrapEngine(); + case "DESEDERFC3211WRAP": + return new Rfc3211WrapEngine(new DesEdeEngine()); + case "AESRFC3211WRAP": + return new Rfc3211WrapEngine(new AesFastEngine()); + case "CAMELLIARFC3211WRAP": + return new Rfc3211WrapEngine(new CamelliaEngine()); + } + + // Create an IBufferedCipher and use it as IWrapper (via BufferedCipherWrapper) + IBufferedCipher blockCipher = CipherUtilities.GetCipher(algorithm); + + if (blockCipher != null) + return new BufferedCipherWrapper(blockCipher); + + throw new SecurityUtilityException("Wrapper " + algorithm + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + private class BufferedCipherWrapper + : IWrapper + { + private readonly IBufferedCipher cipher; + private bool forWrapping; + + public BufferedCipherWrapper( + IBufferedCipher cipher) + { + this.cipher = cipher; + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public void Init( + bool forWrapping, + ICipherParameters parameters) + { + this.forWrapping = forWrapping; + + cipher.Init(forWrapping, parameters); + } + + public byte[] Wrap( + byte[] input, + int inOff, + int length) + { + if (!forWrapping) + throw new InvalidOperationException("Not initialised for wrapping"); + + return cipher.DoFinal(input, inOff, length); + } + + public byte[] Unwrap( + byte[] input, + int inOff, + int length) + { + if (forWrapping) + throw new InvalidOperationException("Not initialised for Unwrapping"); + + return cipher.DoFinal(input, inOff, length); + } + } + } +} diff --git a/src/core/srcbc/security/cert/CertificateEncodingException.cs b/src/core/srcbc/security/cert/CertificateEncodingException.cs new file mode 100644 index 0000000..0b7f4f9 --- /dev/null +++ b/src/core/srcbc/security/cert/CertificateEncodingException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateEncodingException : CertificateException + { + public CertificateEncodingException() : base() { } + public CertificateEncodingException(string msg) : base(msg) { } + public CertificateEncodingException(string msg, Exception e) : base(msg, e) { } + } +} diff --git a/src/core/srcbc/security/cert/CertificateException.cs b/src/core/srcbc/security/cert/CertificateException.cs new file mode 100644 index 0000000..9a3d9b2 --- /dev/null +++ b/src/core/srcbc/security/cert/CertificateException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateException : GeneralSecurityException + { + public CertificateException() : base() { } + public CertificateException(string message) : base(message) { } + public CertificateException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/security/cert/CertificateExpiredException.cs b/src/core/srcbc/security/cert/CertificateExpiredException.cs new file mode 100644 index 0000000..8c2458c --- /dev/null +++ b/src/core/srcbc/security/cert/CertificateExpiredException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateExpiredException : CertificateException + { + public CertificateExpiredException() : base() { } + public CertificateExpiredException(string message) : base(message) { } + public CertificateExpiredException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/security/cert/CertificateNotYetValidException.cs b/src/core/srcbc/security/cert/CertificateNotYetValidException.cs new file mode 100644 index 0000000..4906f64 --- /dev/null +++ b/src/core/srcbc/security/cert/CertificateNotYetValidException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateNotYetValidException : CertificateException + { + public CertificateNotYetValidException() : base() { } + public CertificateNotYetValidException(string message) : base(message) { } + public CertificateNotYetValidException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/security/cert/CertificateParsingException.cs b/src/core/srcbc/security/cert/CertificateParsingException.cs new file mode 100644 index 0000000..a0c7d8f --- /dev/null +++ b/src/core/srcbc/security/cert/CertificateParsingException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateParsingException : CertificateException + { + public CertificateParsingException() : base() { } + public CertificateParsingException(string message) : base(message) { } + public CertificateParsingException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/src/core/srcbc/security/cert/CrlException.cs b/src/core/srcbc/security/cert/CrlException.cs new file mode 100644 index 0000000..2b9b194 --- /dev/null +++ b/src/core/srcbc/security/cert/CrlException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CrlException : GeneralSecurityException + { + public CrlException() : base() { } + public CrlException(string msg) : base(msg) {} + public CrlException(string msg, Exception e) : base(msg, e) {} + } +} diff --git a/src/core/srcbc/tsp/GenTimeAccuracy.cs b/src/core/srcbc/tsp/GenTimeAccuracy.cs new file mode 100644 index 0000000..1bfe53d --- /dev/null +++ b/src/core/srcbc/tsp/GenTimeAccuracy.cs @@ -0,0 +1,33 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Tsp; + +namespace Org.BouncyCastle.Tsp +{ + public class GenTimeAccuracy + { + private Accuracy accuracy; + + public GenTimeAccuracy( + Accuracy accuracy) + { + this.accuracy = accuracy; + } + + public int Seconds { get { return GetTimeComponent(accuracy.Seconds); } } + + public int Millis { get { return GetTimeComponent(accuracy.Millis); } } + + public int Micros { get { return GetTimeComponent(accuracy.Micros); } } + + private int GetTimeComponent( + DerInteger time) + { + return time == null ? 0 : time.Value.IntValue; + } + + public override string ToString() + { + return Seconds + "." + Millis.ToString("000") + Micros.ToString("000"); + } + } +} diff --git a/src/core/srcbc/tsp/TSPAlgorithms.cs b/src/core/srcbc/tsp/TSPAlgorithms.cs new file mode 100644 index 0000000..ee5c4b5 --- /dev/null +++ b/src/core/srcbc/tsp/TSPAlgorithms.cs @@ -0,0 +1,47 @@ +using System.Collections; + +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Recognised hash algorithms for the time stamp protocol. + */ + public abstract class TspAlgorithms + { + public static readonly string MD5 = PkcsObjectIdentifiers.MD5.Id; + + public static readonly string Sha1 = OiwObjectIdentifiers.IdSha1.Id; + + public static readonly string Sha224 = NistObjectIdentifiers.IdSha224.Id; + public static readonly string Sha256 = NistObjectIdentifiers.IdSha256.Id; + public static readonly string Sha384 = NistObjectIdentifiers.IdSha384.Id; + public static readonly string Sha512 = NistObjectIdentifiers.IdSha512.Id; + + public static readonly string RipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id; + public static readonly string RipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id; + public static readonly string RipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id; + + public static readonly string Gost3411 = CryptoProObjectIdentifiers.GostR3411.Id; + + public static readonly ArrayList Allowed; + + static TspAlgorithms() + { + string[] algs = new string[] + { + Gost3411, MD5, Sha1, Sha224, Sha256, Sha384, Sha512, RipeMD128, RipeMD160, RipeMD256 + }; + + Allowed = new ArrayList(); + foreach (string alg in algs) + { + Allowed.Add(alg); + } + } + } +} diff --git a/src/core/srcbc/tsp/TSPException.cs b/src/core/srcbc/tsp/TSPException.cs new file mode 100644 index 0000000..22f1041 --- /dev/null +++ b/src/core/srcbc/tsp/TSPException.cs @@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Tsp +{ + public class TspException + : Exception + { + public TspException() + { + } + + public TspException( + string message) + : base(message) + { + } + + public TspException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/src/core/srcbc/tsp/TSPUtil.cs b/src/core/srcbc/tsp/TSPUtil.cs new file mode 100644 index 0000000..6bc5403 --- /dev/null +++ b/src/core/srcbc/tsp/TSPUtil.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Tsp +{ + public class TspUtil + { + private static readonly IDictionary digestLengths = new Hashtable(); + private static readonly IDictionary digestNames = new Hashtable(); + + static TspUtil() + { + digestLengths.Add(PkcsObjectIdentifiers.MD5.Id, 16); + digestLengths.Add(OiwObjectIdentifiers.IdSha1.Id, 20); + digestLengths.Add(NistObjectIdentifiers.IdSha224.Id, 28); + digestLengths.Add(NistObjectIdentifiers.IdSha256.Id, 32); + digestLengths.Add(NistObjectIdentifiers.IdSha384.Id, 48); + digestLengths.Add(NistObjectIdentifiers.IdSha512.Id, 64); + + digestNames.Add(PkcsObjectIdentifiers.MD5.Id, "MD5"); + digestNames.Add(OiwObjectIdentifiers.IdSha1.Id, "SHA1"); + digestNames.Add(NistObjectIdentifiers.IdSha224.Id, "SHA224"); + digestNames.Add(NistObjectIdentifiers.IdSha256.Id, "SHA256"); + digestNames.Add(NistObjectIdentifiers.IdSha384.Id, "SHA384"); + digestNames.Add(NistObjectIdentifiers.IdSha512.Id, "SHA512"); + digestNames.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption.Id, "SHA1"); + digestNames.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption.Id, "SHA224"); + digestNames.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id, "SHA256"); + digestNames.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption.Id, "SHA384"); + digestNames.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption.Id, "SHA512"); + digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD128.Id, "RIPEMD128"); + digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, "RIPEMD160"); + digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, "RIPEMD256"); + digestNames.Add(CryptoProObjectIdentifiers.GostR3411.Id, "GOST3411"); + } + + /** + * Validate the passed in certificate as being of the correct type to be used + * for time stamping. To be valid it must have an ExtendedKeyUsage extension + * which has a key purpose identifier of id-kp-timeStamping. + * + * @param cert the certificate of interest. + * @throws TspValidationException if the certicate fails on one of the check points. + */ + public static void ValidateCertificate( + X509Certificate cert) + { + if (cert.Version != 3) + throw new ArgumentException("Certificate must have an ExtendedKeyUsage extension."); + + Asn1OctetString ext = cert.GetExtensionValue(X509Extensions.ExtendedKeyUsage); + if (ext == null) + throw new TspValidationException("Certificate must have an ExtendedKeyUsage extension."); + + if (!cert.GetCriticalExtensionOids().Contains(X509Extensions.ExtendedKeyUsage.Id)) + throw new TspValidationException("Certificate must have an ExtendedKeyUsage extension marked as critical."); + + try + { + ExtendedKeyUsage extKey = ExtendedKeyUsage.GetInstance( + Asn1Object.FromByteArray(ext.GetOctets())); + + if (!extKey.HasKeyPurposeId(KeyPurposeID.IdKPTimeStamping) || extKey.Count != 1) + throw new TspValidationException("ExtendedKeyUsage not solely time stamping."); + } + catch (IOException) + { + throw new TspValidationException("cannot process ExtendedKeyUsage extension"); + } + } + + /// + /// Return the digest algorithm using one of the standard JCA string + /// representations rather than the algorithm identifier (if possible). + /// + internal static string GetDigestAlgName( + string digestAlgOID) + { + string digestName = (string) digestNames[digestAlgOID]; + + return digestName != null ? digestName : digestAlgOID; + } + + internal static int GetDigestLength( + string digestAlgOID) + { + string digestName = GetDigestAlgName(digestAlgOID); + + try + { + if (digestLengths.Contains(digestAlgOID)) + { + return (int) digestLengths[digestAlgOID]; + } + + return DigestUtilities.GetDigest(digestName).GetDigestSize(); + } + catch (SecurityUtilityException e) + { + throw new TspException("digest algorithm cannot be found.", e); + } + } + } +} diff --git a/src/core/srcbc/tsp/TSPValidationException.cs b/src/core/srcbc/tsp/TSPValidationException.cs new file mode 100644 index 0000000..c59868a --- /dev/null +++ b/src/core/srcbc/tsp/TSPValidationException.cs @@ -0,0 +1,39 @@ +namespace Org.BouncyCastle.Tsp +{ + /** + * Exception thrown if a TSP request or response fails to validate. + *

    + * If a failure code is assciated with the exception it can be retrieved using + * the getFailureCode() method.

    + */ + public class TspValidationException + : TspException + { + private int failureCode; + + public TspValidationException( + string message) + : base(message) + { + this.failureCode = -1; + } + + public TspValidationException( + string message, + int failureCode) + : base(message) + { + this.failureCode = failureCode; + } + + /** + * Return the failure code associated with this exception - if one is set. + * + * @return the failure code if set, -1 otherwise. + */ + public int FailureCode + { + get { return failureCode; } + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampRequest.cs b/src/core/srcbc/tsp/TimeStampRequest.cs new file mode 100644 index 0000000..84cf311 --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampRequest.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Base class for an RFC 3161 Time Stamp Request. + */ + public class TimeStampRequest + : X509ExtensionBase + { + private TimeStampReq req; + + public TimeStampRequest( + TimeStampReq req) + { + this.req = req; + } + + /** + * Create a TimeStampRequest from the past in byte array. + * + * @param req byte array containing the request. + * @throws IOException if the request is malformed. + */ + public TimeStampRequest( + byte[] req) + : this(new Asn1InputStream(req)) + { + } + + /** + * Create a TimeStampRequest from the past in input stream. + * + * @param in input stream containing the request. + * @throws IOException if the request is malformed. + */ + public TimeStampRequest( + Stream input) + : this(new Asn1InputStream(input)) + { + } + + private TimeStampRequest( + Asn1InputStream str) + { + try + { + this.req = TimeStampReq.GetInstance(str.ReadObject()); + } + catch (InvalidCastException e) + { + throw new IOException("malformed request: " + e); + } + catch (ArgumentException e) + { + throw new IOException("malformed request: " + e); + } + } + + public int Version + { + get { return req.Version.Value.IntValue; } + } + + public string MessageImprintAlgOid + { + get { return req.MessageImprint.HashAlgorithm.ObjectID.Id; } + } + + public byte[] GetMessageImprintDigest() + { + return req.MessageImprint.GetHashedMessage(); + } + + public string ReqPolicy + { + get + { + return req.ReqPolicy == null + ? null + : req.ReqPolicy.Id; + } + } + + public BigInteger Nonce + { + get + { + return req.Nonce == null + ? null + : req.Nonce.Value; + } + } + + public bool CertReq + { + get + { + return req.CertReq == null + ? false + : req.CertReq.IsTrue; + } + } + + /** + * Validate the timestamp request, checking the digest to see if it is of an + * accepted type and whether it is of the correct length for the algorithm specified. + * + * @param algorithms a set of string OIDS giving accepted algorithms. + * @param policies if non-null a set of policies we are willing to sign under. + * @param extensions if non-null a set of extensions we are willing to accept. + * @throws TspException if the request is invalid, or processing fails. + */ + public void Validate( + IList algorithms, + IList policies, + IList extensions) + { + if (!algorithms.Contains(this.MessageImprintAlgOid)) + { + throw new TspValidationException("request contains unknown algorithm.", PkiFailureInfo.BadAlg); + } + + if (policies != null && this.ReqPolicy != null && !policies.Contains(this.ReqPolicy)) + { + throw new TspValidationException("request contains unknown policy.", PkiFailureInfo.UnacceptedPolicy); + } + + if (this.Extensions != null && extensions != null) + { + foreach (DerObjectIdentifier oid in this.Extensions.ExtensionOids) + { + if (!extensions.Contains(oid.Id)) + { + throw new TspValidationException("request contains unknown extension.", + PkiFailureInfo.UnacceptedExtension); + } + } + } + + int digestLength = TspUtil.GetDigestLength(this.MessageImprintAlgOid); + + if (digestLength != this.GetMessageImprintDigest().Length) + { + throw new TspValidationException("imprint digest the wrong length.", + PkiFailureInfo.BadDataFormat); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return req.GetEncoded(); + } + + internal X509Extensions Extensions + { + get { return req.Extensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return Extensions; + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampRequestGenerator.cs b/src/core/srcbc/tsp/TimeStampRequestGenerator.cs new file mode 100644 index 0000000..eadceb5 --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampRequestGenerator.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Generator for RFC 3161 Time Stamp Request objects. + */ + public class TimeStampRequestGenerator + { + private DerObjectIdentifier reqPolicy; + + private DerBoolean certReq; + + private Hashtable extensions = new Hashtable(); + private ArrayList extOrdering = new ArrayList(); + + public void SetReqPolicy( + string reqPolicy) + { + this.reqPolicy = new DerObjectIdentifier(reqPolicy); + } + + public void SetCertReq( + bool certReq) + { + this.certReq = DerBoolean.GetInstance(certReq); + } + + /** + * add a given extension field for the standard extensions tag (tag 3) + * @throws IOException + */ + public void AddExtension( + string oid, + bool critical, + Asn1Encodable value) + { + this.AddExtension(oid, critical, value.GetEncoded()); + } + + /** + * add a given extension field for the standard extensions tag + * The value parameter becomes the contents of the octet string associated + * with the extension. + */ + public void AddExtension( + string oid, + bool critical, + byte[] value) + { + DerObjectIdentifier derOid = new DerObjectIdentifier(oid); + extensions[derOid] = new X509Extension(critical, new DerOctetString(value)); + extOrdering.Add(derOid); + } + + public TimeStampRequest Generate( + string digestAlgorithm, + byte[] digest) + { + return this.Generate(digestAlgorithm, digest, null); + } + + public TimeStampRequest Generate( + string digestAlgorithmOid, + byte[] digest, + BigInteger nonce) + { + if (digestAlgorithmOid == null) + { + throw new ArgumentException("No digest algorithm specified"); + } + + DerObjectIdentifier digestAlgOid = new DerObjectIdentifier(digestAlgorithmOid); + + AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOid, DerNull.Instance); + MessageImprint messageImprint = new MessageImprint(algID, digest); + + X509Extensions ext = null; + + if (extOrdering.Count != 0) + { + ext = new X509Extensions(extOrdering, extensions); + } + + DerInteger derNonce = nonce == null + ? null + : new DerInteger(nonce); + + return new TimeStampRequest( + new TimeStampReq(messageImprint, reqPolicy, derNonce, certReq, ext)); + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampResponse.cs b/src/core/srcbc/tsp/TimeStampResponse.cs new file mode 100644 index 0000000..37971ac --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampResponse.cs @@ -0,0 +1,173 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Base class for an RFC 3161 Time Stamp Response object. + */ + public class TimeStampResponse + { + private TimeStampResp resp; + private TimeStampToken timeStampToken; + + public TimeStampResponse( + TimeStampResp resp) + { + this.resp = resp; + + if (resp.TimeStampToken != null) + { + timeStampToken = new TimeStampToken(resp.TimeStampToken); + } + } + + /** + * Create a TimeStampResponse from a byte array containing an ASN.1 encoding. + * + * @param resp the byte array containing the encoded response. + * @throws TspException if the response is malformed. + * @throws IOException if the byte array doesn't represent an ASN.1 encoding. + */ + public TimeStampResponse( + byte[] resp) + : this(readTimeStampResp(new Asn1InputStream(resp))) + { + } + + /** + * Create a TimeStampResponse from an input stream containing an ASN.1 encoding. + * + * @param input the input stream containing the encoded response. + * @throws TspException if the response is malformed. + * @throws IOException if the stream doesn't represent an ASN.1 encoding. + */ + public TimeStampResponse( + Stream input) + : this(readTimeStampResp(new Asn1InputStream(input))) + { + } + + private static TimeStampResp readTimeStampResp( + Asn1InputStream input) + { + try + { + return TimeStampResp.GetInstance(input.ReadObject()); + } + catch (ArgumentException e) + { + throw new TspException("malformed timestamp response: " + e, e); + } + catch (InvalidCastException e) + { + throw new TspException("malformed timestamp response: " + e, e); + } + } + + public int Status + { + get { return resp.Status.Status.IntValue; } + } + + public string GetStatusString() + { + if (resp.Status.StatusString == null) + { + return null; + } + + StringBuilder statusStringBuf = new StringBuilder(); + PkiFreeText text = resp.Status.StatusString; + for (int i = 0; i != text.Count; i++) + { + statusStringBuf.Append(text[i].GetString()); + } + + return statusStringBuf.ToString(); + } + + public PkiFailureInfo GetFailInfo() + { + if (resp.Status.FailInfo == null) + { + return null; + } + + return new PkiFailureInfo(resp.Status.FailInfo); + } + + public TimeStampToken TimeStampToken + { + get { return timeStampToken; } + } + + /** + * Check this response against to see if it a well formed response for + * the passed in request. Validation will include checking the time stamp + * token if the response status is GRANTED or GRANTED_WITH_MODS. + * + * @param request the request to be checked against + * @throws TspException if the request can not match this response. + */ + public void Validate( + TimeStampRequest request) + { + TimeStampToken tok = this.TimeStampToken; + + if (tok != null) + { + TimeStampTokenInfo tstInfo = tok.TimeStampInfo; + + if (request.Nonce != null && !request.Nonce.Equals(tstInfo.Nonce)) + { + throw new TspValidationException("response contains wrong nonce value."); + } + + if (this.Status != (int) PkiStatus.Granted && this.Status != (int) PkiStatus.GrantedWithMods) + { + throw new TspValidationException("time stamp token found in failed request."); + } + + if (!Arrays.AreEqual(request.GetMessageImprintDigest(), tstInfo.GetMessageImprintDigest())) + { + throw new TspValidationException("response for different message imprint digest."); + } + + if (!tstInfo.MessageImprintAlgOid.Equals(request.MessageImprintAlgOid)) + { + throw new TspValidationException("response for different message imprint algorithm."); + } + + if (tok.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificate] == null) + { + throw new TspValidationException("no signing certificate attribute present."); + } + + if (request.ReqPolicy != null && !request.ReqPolicy.Equals(tstInfo.Policy)) + { + throw new TspValidationException("TSA policy wrong for request."); + } + } + else if (this.Status == (int) PkiStatus.Granted || this.Status == (int) PkiStatus.GrantedWithMods) + { + throw new TspValidationException("no time stamp token found and one expected."); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return resp.GetEncoded(); + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampResponseGenerator.cs b/src/core/srcbc/tsp/TimeStampResponseGenerator.cs new file mode 100644 index 0000000..acd13fd --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampResponseGenerator.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Generator for RFC 3161 Time Stamp Responses. + */ + public class TimeStampResponseGenerator + { + private PkiStatus status; + + private Asn1EncodableVector statusStrings; + + private int failInfo; + private TimeStampTokenGenerator tokenGenerator; + private IList acceptedAlgorithms; + private IList acceptedPolicies; + private IList acceptedExtensions; + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms) + : this(tokenGenerator, acceptedAlgorithms, null, null) + { + } + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms, + IList acceptedPolicy) + : this(tokenGenerator, acceptedAlgorithms, acceptedPolicy, null) + { + } + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms, + IList acceptedPolicies, + IList acceptedExtensions) + { + this.tokenGenerator = tokenGenerator; + this.acceptedAlgorithms = acceptedAlgorithms; + this.acceptedPolicies = acceptedPolicies; + this.acceptedExtensions = acceptedExtensions; + + statusStrings = new Asn1EncodableVector(); + } + + private void addStatusString( + string statusString) + { + statusStrings.Add(new DerUtf8String(statusString)); + } + + private void setFailInfoField(int field) + { + failInfo = failInfo | field; + } + + private PkiStatusInfo getPkiStatusInfo() + { + Asn1EncodableVector v = new Asn1EncodableVector( + new DerInteger((int) status)); + + if (statusStrings.Count > 0) + { + v.Add(new PkiFreeText(new DerSequence(statusStrings))); + } + + if (failInfo != 0) + { + v.Add(new FailInfo(failInfo)); + } + + return new PkiStatusInfo(new DerSequence(v)); + } + + public TimeStampResponse Generate( + TimeStampRequest request, + BigInteger serialNumber, + DateTime genTime) + { + TimeStampResp resp; + + try + { + request.Validate(acceptedAlgorithms, acceptedPolicies, acceptedExtensions); + + status = PkiStatus.Granted; + this.addStatusString("Operation Okay"); + + PkiStatusInfo pkiStatusInfo = getPkiStatusInfo(); + + ContentInfo tstTokenContentInfo; + try + { + TimeStampToken token = tokenGenerator.Generate(request, serialNumber, genTime); + byte[] encoded = token.ToCmsSignedData().GetEncoded(); + + tstTokenContentInfo = ContentInfo.GetInstance(Asn1Object.FromByteArray(encoded)); + } + catch (IOException ioEx) + { + throw new TspException( + "Timestamp token received cannot be converted to ContentInfo", ioEx); + } + + resp = new TimeStampResp(pkiStatusInfo, tstTokenContentInfo); + } + catch (TspValidationException e) + { + status = PkiStatus.Rejection; + + this.setFailInfoField(e.FailureCode); + this.addStatusString(e.Message); + + PkiStatusInfo pkiStatusInfo = getPkiStatusInfo(); + + resp = new TimeStampResp(pkiStatusInfo, null); + } + + try + { + return new TimeStampResponse(resp); + } + catch (IOException) + { + throw new TspException("created badly formatted response!"); + } + } + + class FailInfo + : DerBitString + { + internal FailInfo( + int failInfoValue) + : base(GetBytes(failInfoValue), GetPadBits(failInfoValue)) + { + } + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampToken.cs b/src/core/srcbc/tsp/TimeStampToken.cs new file mode 100644 index 0000000..af613e0 --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampToken.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ess; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampToken + { + private readonly CmsSignedData tsToken; + private readonly SignerInformation tsaSignerInfo; +// private readonly DateTime genTime; + private readonly TimeStampTokenInfo tstInfo; + private readonly CertID certID; + + public TimeStampToken( + Asn1.Cms.ContentInfo contentInfo) + : this(new CmsSignedData(contentInfo)) + { + } + + public TimeStampToken( + CmsSignedData signedData) + { + this.tsToken = signedData; + + if (!this.tsToken.SignedContentTypeOid.Equals(PkcsObjectIdentifiers.IdCTTstInfo.Id)) + { + throw new TspValidationException("ContentInfo object not for a time stamp."); + } + + ICollection signers = tsToken.GetSignerInfos().GetSigners(); + + if (signers.Count != 1) + { + throw new ArgumentException("Time-stamp token signed by " + + signers.Count + + " signers, but it must contain just the TSA signature."); + } + + + IEnumerator signerEnum = signers.GetEnumerator(); + + signerEnum.MoveNext(); + tsaSignerInfo = (SignerInformation) signerEnum.Current; + + try + { + CmsProcessable content = tsToken.SignedContent; + MemoryStream bOut = new MemoryStream(); + + content.Write(bOut); + + this.tstInfo = new TimeStampTokenInfo( + TstInfo.GetInstance( + Asn1Object.FromByteArray(bOut.ToArray()))); + + Asn1.Cms.Attribute attr = tsaSignerInfo.SignedAttributes[ + PkcsObjectIdentifiers.IdAASigningCertificate]; + +// if (attr == null) +// { +// throw new TspValidationException( +// "no signing certificate attribute found, time stamp invalid."); +// } +// +// SigningCertificate signCert = SigningCertificate.GetInstance( +// attr.AttrValues[0]); +// +// this.certID = EssCertID.GetInstance(signCert.GetCerts()[0]); + + if (attr != null) + { + SigningCertificate signCert = SigningCertificate.GetInstance(attr.AttrValues[0]); + + this.certID = new CertID(EssCertID.GetInstance(signCert.GetCerts()[0])); + } + else + { + attr = tsaSignerInfo.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificateV2]; + + if (attr == null) + throw new TspValidationException("no signing certificate attribute found, time stamp invalid."); + + SigningCertificateV2 signCertV2 = SigningCertificateV2.GetInstance(attr.AttrValues[0]); + + this.certID = new CertID(EssCertIDv2.GetInstance(signCertV2.GetCerts()[0])); + } + } + catch (CmsException e) + { + throw new TspException(e.Message, e.InnerException); + } + } + + public TimeStampTokenInfo TimeStampInfo + { + get { return tstInfo; } + } + + public SignerID SignerID + { + get { return tsaSignerInfo.SignerID; } + } + + public Asn1.Cms.AttributeTable SignedAttributes + { + get { return tsaSignerInfo.SignedAttributes; } + } + + public Asn1.Cms.AttributeTable UnsignedAttributes + { + get { return tsaSignerInfo.UnsignedAttributes; } + } + +// public IX509Store GetCertificatesAndCrls( +// string type) +// { +// return tsToken.GetCertificatesAndCrls(type); +// } + + public IX509Store GetCertificates( + string type) + { + return tsToken.GetCertificates(type); + } + + public IX509Store GetCrls( + string type) + { + return tsToken.GetCrls(type); + } + + /** + * Validate the time stamp token. + *

    + * To be valid the token must be signed by the passed in certificate and + * the certificate must be the one referred to by the SigningCertificate + * attribute included in the hashed attributes of the token. The + * certificate must also have the ExtendedKeyUsageExtension with only + * KeyPurposeID.IdKPTimeStamping and have been valid at the time the + * timestamp was created. + *

    + *

    + * A successful call to validate means all the above are true. + *

    + */ + public void Validate( + X509Certificate cert) + { + IDigest digest; + try + { + digest = DigestUtilities.GetDigest(certID.GetHashAlgorithm()); + } + catch (SecurityUtilityException e) + { + throw new TspException("cannot find algorithm: " + e.Message, e); + } + + try + { + byte[] certEncoded = cert.GetEncoded(); + digest.BlockUpdate(certEncoded, 0, certEncoded.Length); + byte[] hash = DigestUtilities.DoFinal(digest); + + if (!Arrays.AreEqual(certID.GetCertHash(), hash)) + { + throw new TspValidationException("certificate hash does not match certID hash."); + } + + if (certID.IssuerSerial != null) + { + if (!certID.IssuerSerial.Serial.Value.Equals(cert.SerialNumber)) + { + throw new TspValidationException("certificate serial number does not match certID for signature."); + } + + GeneralName[] names = certID.IssuerSerial.Issuer.GetNames(); + X509Name principal = PrincipalUtilities.GetIssuerX509Principal(cert); + bool found = false; + + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == 4 + && X509Name.GetInstance(names[i].Name).Equivalent(principal)) + { + found = true; + break; + } + } + + if (!found) + { + throw new TspValidationException("certificate name does not match certID for signature. "); + } + } + + TspUtil.ValidateCertificate(cert); + + cert.CheckValidity(tstInfo.GenTime); + + if (!tsaSignerInfo.Verify(cert)) + { + throw new TspValidationException("signature not created by certificate."); + } + } + catch (CmsException e) + { + if (e.InnerException != null) + { + throw new TspException(e.Message, e.InnerException); + } + + throw new TspException("CMS exception: " + e, e); + } + catch (CertificateEncodingException e) + { + throw new TspException("problem processing certificate: " + e, e); + } + } + + /** + * Return the underlying CmsSignedData object. + * + * @return the underlying CMS structure. + */ + public CmsSignedData ToCmsSignedData() + { + return tsToken; + } + + /** + * Return a ASN.1 encoded byte stream representing the encoded object. + * + * @throws IOException if encoding fails. + */ + public byte[] GetEncoded() + { + return tsToken.GetEncoded(); + } + + + // perhaps this should be done using an interface on the ASN.1 classes... + private class CertID + { + private EssCertID certID; + private EssCertIDv2 certIDv2; + + internal CertID(EssCertID certID) + { + this.certID = certID; + this.certIDv2 = null; + } + + internal CertID(EssCertIDv2 certID) + { + this.certIDv2 = certID; + this.certID = null; + } + + public string GetHashAlgorithm() + { + if (certID != null) + return "SHA-1"; + + if (NistObjectIdentifiers.IdSha256.Equals(certIDv2.HashAlgorithm.ObjectID)) + return "SHA-256"; + + return certIDv2.HashAlgorithm.ObjectID.Id; + } + + public byte[] GetCertHash() + { + return certID != null + ? certID.GetCertHash() + : certIDv2.GetCertHash(); + } + + public IssuerSerial IssuerSerial + { + get + { + return certID != null + ? certID.IssuerSerial + : certIDv2.IssuerSerial; + } + } + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampTokenGenerator.cs b/src/core/srcbc/tsp/TimeStampTokenGenerator.cs new file mode 100644 index 0000000..a2a8ef9 --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampTokenGenerator.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ess; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampTokenGenerator + { + private int accuracySeconds = -1; + private int accuracyMillis = -1; + private int accuracyMicros = -1; + private bool ordering = false; + private GeneralName tsa = null; + private string tsaPolicyOID; + + private AsymmetricKeyParameter key; + private X509Certificate cert; + private string digestOID; + private Asn1.Cms.AttributeTable signedAttr; + private Asn1.Cms.AttributeTable unsignedAttr; + private IX509Store x509Certs; + private IX509Store x509Crls; + + /** + * basic creation - only the default attributes will be included here. + */ + public TimeStampTokenGenerator( + AsymmetricKeyParameter key, + X509Certificate cert, + string digestOID, + string tsaPolicyOID) + : this(key, cert, digestOID, tsaPolicyOID, null, null) + { + } + + /** + * create with a signer with extra signed/unsigned attributes. + */ + public TimeStampTokenGenerator( + AsymmetricKeyParameter key, + X509Certificate cert, + string digestOID, + string tsaPolicyOID, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + this.key = key; + this.cert = cert; + this.digestOID = digestOID; + this.tsaPolicyOID = tsaPolicyOID; + this.unsignedAttr = unsignedAttr; + + TspUtil.ValidateCertificate(cert); + + // + // add the essCertid + // + Hashtable signedAttrs; + if (signedAttr != null) + { + signedAttrs = signedAttr.ToHashtable(); + } + else + { + signedAttrs = new Hashtable(); + } + + IDigest digest; + try + { + digest = DigestUtilities.GetDigest("SHA-1"); + } + catch (Exception e) + { + throw new TspException("Can't find a SHA-1 implementation.", e); + } + + try + { + byte[] certEncoded = cert.GetEncoded(); + digest.BlockUpdate(certEncoded, 0, certEncoded.Length); + byte[] hash = DigestUtilities.DoFinal(digest); + + EssCertID essCertid = new EssCertID(hash); + + Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute( + PkcsObjectIdentifiers.IdAASigningCertificate, + new DerSet(new SigningCertificate(essCertid))); + + signedAttrs[attr.AttrType] = attr; + } + catch (CertificateEncodingException e) + { + throw new TspException("Exception processing certificate.", e); + } + + this.signedAttr = new Asn1.Cms.AttributeTable(signedAttrs); + } + + public void SetCertificates( + IX509Store certificates) + { + this.x509Certs = certificates; + } + + public void SetCrls( + IX509Store crls) + { + this.x509Crls = crls; + } + + public void SetAccuracySeconds( + int accuracySeconds) + { + this.accuracySeconds = accuracySeconds; + } + + public void SetAccuracyMillis( + int accuracyMillis) + { + this.accuracyMillis = accuracyMillis; + } + + public void SetAccuracyMicros( + int accuracyMicros) + { + this.accuracyMicros = accuracyMicros; + } + + public void SetOrdering( + bool ordering) + { + this.ordering = ordering; + } + + public void SetTsa( + GeneralName tsa) + { + this.tsa = tsa; + } + + //------------------------------------------------------------------------------ + + public TimeStampToken Generate( + TimeStampRequest request, + BigInteger serialNumber, + DateTime genTime) + { + DerObjectIdentifier digestAlgOID = new DerObjectIdentifier(request.MessageImprintAlgOid); + + AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DerNull.Instance); + MessageImprint messageImprint = new MessageImprint(algID, request.GetMessageImprintDigest()); + + Accuracy accuracy = null; + if (accuracySeconds > 0 || accuracyMillis > 0 || accuracyMicros > 0) + { + DerInteger seconds = null; + if (accuracySeconds > 0) + { + seconds = new DerInteger(accuracySeconds); + } + + DerInteger millis = null; + if (accuracyMillis > 0) + { + millis = new DerInteger(accuracyMillis); + } + + DerInteger micros = null; + if (accuracyMicros > 0) + { + micros = new DerInteger(accuracyMicros); + } + + accuracy = new Accuracy(seconds, millis, micros); + } + + DerBoolean derOrdering = null; + if (ordering) + { + derOrdering = DerBoolean.GetInstance(ordering); + } + + DerInteger nonce = null; + if (request.Nonce != null) + { + nonce = new DerInteger(request.Nonce); + } + + DerObjectIdentifier tsaPolicy = new DerObjectIdentifier(tsaPolicyOID); + if (request.ReqPolicy != null) + { + tsaPolicy = new DerObjectIdentifier(request.ReqPolicy); + } + + TstInfo tstInfo = new TstInfo(tsaPolicy, messageImprint, + new DerInteger(serialNumber), new DerGeneralizedTime(genTime), accuracy, + derOrdering, nonce, tsa, request.Extensions); + + try + { + CmsSignedDataGenerator signedDataGenerator = new CmsSignedDataGenerator(); + + byte[] derEncodedTstInfo = tstInfo.GetDerEncoded(); + + if (request.CertReq) + { + signedDataGenerator.AddCertificates(x509Certs); + } + + signedDataGenerator.AddCrls(x509Crls); + signedDataGenerator.AddSigner(key, cert, digestOID, signedAttr, unsignedAttr); + + CmsSignedData signedData = signedDataGenerator.Generate( + PkcsObjectIdentifiers.IdCTTstInfo.Id, + new CmsProcessableByteArray(derEncodedTstInfo), + true); + + return new TimeStampToken(signedData); + } + catch (CmsException cmsEx) + { + throw new TspException("Error generating time-stamp token", cmsEx); + } + catch (IOException e) + { + throw new TspException("Exception encoding info", e); + } + catch (X509StoreException e) + { + throw new TspException("Exception handling CertStore", e); + } +// catch (InvalidAlgorithmParameterException e) +// { +// throw new TspException("Exception handling CertStore CRLs", e); +// } + } + } +} diff --git a/src/core/srcbc/tsp/TimeStampTokenInfo.cs b/src/core/srcbc/tsp/TimeStampTokenInfo.cs new file mode 100644 index 0000000..a2391ca --- /dev/null +++ b/src/core/srcbc/tsp/TimeStampTokenInfo.cs @@ -0,0 +1,102 @@ +using System; + +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampTokenInfo + { + private TstInfo tstInfo; + private DateTime genTime; + + public TimeStampTokenInfo( + TstInfo tstInfo) + { + this.tstInfo = tstInfo; + + try + { + this.genTime = tstInfo.GenTime.ToDateTime(); + } + catch (Exception e) + { + throw new TspException("unable to parse genTime field: " + e.Message); + } + } + + public bool IsOrdered + { + get { return tstInfo.Ordering.IsTrue; } + } + + public Accuracy Accuracy + { + get { return tstInfo.Accuracy; } + } + + public DateTime GenTime + { + get { return genTime; } + } + + public GenTimeAccuracy GenTimeAccuracy + { + get + { + return this.Accuracy == null + ? null + : new GenTimeAccuracy(this.Accuracy); + } + } + + public string Policy + { + get { return tstInfo.Policy.Id; } + } + + public BigInteger SerialNumber + { + get { return tstInfo.SerialNumber.Value; } + } + + public GeneralName Tsa + { + get { return tstInfo.Tsa; } + } + + /** + * @return the nonce value, null if there isn't one. + */ + public BigInteger Nonce + { + get + { + return tstInfo.Nonce == null + ? null + : tstInfo.Nonce.Value; + } + } + + public string MessageImprintAlgOid + { + get { return tstInfo.MessageImprint.HashAlgorithm.ObjectID.Id; } + } + + public byte[] GetMessageImprintDigest() + { + return tstInfo.MessageImprint.GetHashedMessage(); + } + + public byte[] GetEncoded() + { + return tstInfo.GetEncoded(); + } + + public TstInfo TstInfo + { + get { return tstInfo; } + } + } +} diff --git a/src/core/srcbc/util/Arrays.cs b/src/core/srcbc/util/Arrays.cs new file mode 100644 index 0000000..af16246 --- /dev/null +++ b/src/core/srcbc/util/Arrays.cs @@ -0,0 +1,134 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Utilities +{ + + /// General array utilities. + public sealed class Arrays + { + private Arrays() + { + } + + /// + /// Are two arrays equal. + /// + /// Left side. + /// Right side. + /// True if equal. + public static bool AreEqual( + byte[] a, + byte[] b) + { + if (a == b) + return true; + + if (a == null || b == null) + return false; + + return HaveSameContents(a, b); + } + + [Obsolete("Use 'AreEqual' method instead")] + public static bool AreSame( + byte[] a, + byte[] b) + { + return AreEqual(a, b); + } + + public static bool AreEqual( + int[] a, + int[] b) + { + if (a == b) + return true; + + if (a == null || b == null) + return false; + + return HaveSameContents(a, b); + } + + private static bool HaveSameContents( + 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 static bool HaveSameContents( + int[] a, + int[] 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; + } + + public static string ToString( + object[] a) + { + StringBuilder sb = new StringBuilder('['); + if (a.Length > 0) + { + sb.Append(a[0]); + for (int index = 1; index < a.Length; ++index) + { + sb.Append(", ").Append(a[index]); + } + } + sb.Append(']'); + return sb.ToString(); + } + + public static int GetHashCode( + byte[] data) + { + if (data == null) + { + return 0; + } + + int i = data.Length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static byte[] Clone( + byte[] data) + { + return data == null ? null : (byte[]) data.Clone(); + } + + public static int[] Clone( + int[] data) + { + return data == null ? null : (int[]) data.Clone(); + } + } +} diff --git a/src/core/srcbc/util/BigIntegers.cs b/src/core/srcbc/util/BigIntegers.cs new file mode 100644 index 0000000..b3c57a0 --- /dev/null +++ b/src/core/srcbc/util/BigIntegers.cs @@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Utilities +{ + /** + * BigInteger utilities. + */ + public sealed class BigIntegers + { + private BigIntegers() + { + } + + /** + * Return the passed in value as an unsigned byte array. + * + * @param value value to be converted. + * @return a byte array without a leading zero byte if present in the signed encoding. + */ + public static byte[] AsUnsignedByteArray( + BigInteger n) + { + return n.ToByteArrayUnsigned(); + } + } +} diff --git a/src/core/srcbc/util/Platform.cs b/src/core/srcbc/util/Platform.cs new file mode 100644 index 0000000..e52fc96 --- /dev/null +++ b/src/core/srcbc/util/Platform.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Utilities +{ + internal sealed class Platform + { + private Platform() + { + } + +#if NETCF_1_0 + internal static Exception CreateNotImplementedException( + string message) + { + return new Exception("Not implemented: " + message); + } + + internal static bool Equals( + object a, + object b) + { + return a == b || (a != null && b != null && a.Equals(b)); + } + + internal static string GetEnvironmentVariable( + string variable) + { + return null; + } + + private static string GetNewLine() + { + MemoryStream buf = new MemoryStream(); + StreamWriter w = new StreamWriter(buf, Encoding.ASCII); + w.WriteLine(); + w.Close(); + byte[] bs = buf.ToArray(); + return Encoding.ASCII.GetString(bs, 0, bs.Length); + } +#else + internal static Exception CreateNotImplementedException( + string message) + { + return new NotImplementedException(message); + } + + internal static string GetEnvironmentVariable( + string variable) + { + try + { + return Environment.GetEnvironmentVariable(variable); + } + catch (System.Security.SecurityException) + { + // We don't have the required permission to read this environment variable, + // which is fine, just act as if it's not set + return null; + } + } + + private static string GetNewLine() + { + return Environment.NewLine; + } +#endif + + internal static readonly string NewLine = GetNewLine(); + } +} diff --git a/src/core/srcbc/util/Strings.cs b/src/core/srcbc/util/Strings.cs new file mode 100644 index 0000000..491ddad --- /dev/null +++ b/src/core/srcbc/util/Strings.cs @@ -0,0 +1,34 @@ +using System; + +namespace Org.BouncyCastle.Utilities +{ + /// General string utilities. + public sealed class Strings + { + private Strings() + { + } + + public static string FromByteArray( + byte[] bs) + { + char[] cs = new char[bs.Length]; + for (int i = 0; i < cs.Length; ++i) + { + cs[i] = Convert.ToChar(bs[i]); + } + return new string(cs); + } + + public static byte[] ToByteArray( + string s) + { + byte[] bs = new byte[s.Length]; + for (int i = 0; i < bs.Length; ++i) + { + bs[i] = Convert.ToByte(s[i]); + } + return bs; + } + } +} diff --git a/src/core/srcbc/util/bzip2/BZip2Constants.cs b/src/core/srcbc/util/bzip2/BZip2Constants.cs new file mode 100644 index 0000000..0cbadb9 --- /dev/null +++ b/src/core/srcbc/util/bzip2/BZip2Constants.cs @@ -0,0 +1,139 @@ +using System; + +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Ant" and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * to whom the Ant project is very grateful for his + * great code. + */ + +namespace Org.BouncyCastle.Apache.Bzip2 +{ + /** + * Base class for both the compress and decompress classes. + * Holds common arrays, and static data. + * + * @author
    Keiron Liddle + */ + public class BZip2Constants { + + public const int baseBlockSize = 100000; + public const int MAX_ALPHA_SIZE = 258; + public const int MAX_CODE_LEN = 23; + public const int RUNA = 0; + public const int RUNB = 1; + public const int N_GROUPS = 6; + public const int G_SIZE = 50; + public const int N_ITERS = 4; + public const int MAX_SELECTORS = (2 + (900000 / G_SIZE)); + public const int NUM_OVERSHOOT_BYTES = 20; + + public static int[] rNums = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 + }; + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/bzip2/CBZip2InputStream.cs b/src/core/srcbc/util/bzip2/CBZip2InputStream.cs new file mode 100644 index 0000000..b3773c1 --- /dev/null +++ b/src/core/srcbc/util/bzip2/CBZip2InputStream.cs @@ -0,0 +1,954 @@ +using System; +using System.IO; +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Ant" and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * to whom the Ant project is very grateful for his + * great code. + */ + +namespace Org.BouncyCastle.Apache.Bzip2 +{ + /** + * An input stream that decompresses from the BZip2 format (with the file + * header chars) to be read as any other stream. + * + * @author Keiron Liddle + * + * NB: note this class has been modified to read the leading BZ from the + * start of the BZIP2 stream to make it compatible with other PGP programs. + */ + public class CBZip2InputStream : Stream + { + private static void Cadvise() { + //System.out.Println("CRC Error"); + //throw new CCoruptionError(); + } + + private static void BadBGLengths() { + Cadvise(); + } + + private static void BitStreamEOF() { + Cadvise(); + } + + private static void CompressedStreamEOF() { + Cadvise(); + } + + private void MakeMaps() { + int i; + nInUse = 0; + for (i = 0; i < 256; i++) { + if (inUse[i]) { + seqToUnseq[nInUse] = (char) i; + unseqToSeq[i] = (char) nInUse; + nInUse++; + } + } + } + + /* + index of the last char in the block, so + the block size == last + 1. + */ + private int last; + + /* + index in zptr[] of original string after sorting. + */ + private int origPtr; + + /* + always: in the range 0 .. 9. + The current block size is 100000 * this number. + */ + private int blockSize100k; + + private bool blockRandomised; + + private int bsBuff; + private int bsLive; + private CRC mCrc = new CRC(); + + private bool[] inUse = new bool[256]; + private int nInUse; + + private char[] seqToUnseq = new char[256]; + private char[] unseqToSeq = new char[256]; + + private char[] selector = new char[BZip2Constants.MAX_SELECTORS]; + private char[] selectorMtf = new char[BZip2Constants.MAX_SELECTORS]; + + private int[] tt; + private char[] ll8; + + /* + freq table collected to save a pass over the data + during decompression. + */ + private int[] unzftab = new int[256]; + + private int[][] limit = InitIntArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); + private int[][] basev = InitIntArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); + private int[][] perm = InitIntArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); + private int[] minLens = new int[BZip2Constants.N_GROUPS]; + + private Stream bsStream; + + private bool streamEnd = false; + + private int currentChar = -1; + + private const int START_BLOCK_STATE = 1; + private const int RAND_PART_A_STATE = 2; + private const int RAND_PART_B_STATE = 3; + private const int RAND_PART_C_STATE = 4; + private const int NO_RAND_PART_A_STATE = 5; + private const int NO_RAND_PART_B_STATE = 6; + private const int NO_RAND_PART_C_STATE = 7; + + private int currentState = START_BLOCK_STATE; + + private int storedBlockCRC, storedCombinedCRC; + private int computedBlockCRC, computedCombinedCRC; + + int i2, count, chPrev, ch2; + int i, tPos; + int rNToGo = 0; + int rTPos = 0; + int j2; + char z; + + public CBZip2InputStream(Stream zStream) { + ll8 = null; + tt = null; + BsSetStream(zStream); + Initialize(); + InitBlock(); + SetupBlock(); + } + + internal static int[][] InitIntArray(int n1, int n2) { + int[][] a = new int[n1][]; + for (int k = 0; k < n1; ++k) { + a[k] = new int[n2]; + } + return a; + } + + internal static char[][] InitCharArray(int n1, int n2) { + char[][] a = new char[n1][]; + for (int k = 0; k < n1; ++k) { + a[k] = new char[n2]; + } + return a; + } + + public override int ReadByte() { + if (streamEnd) { + return -1; + } else { + int retChar = currentChar; + switch (currentState) { + case START_BLOCK_STATE: + break; + case RAND_PART_A_STATE: + break; + case RAND_PART_B_STATE: + SetupRandPartB(); + break; + case RAND_PART_C_STATE: + SetupRandPartC(); + break; + case NO_RAND_PART_A_STATE: + break; + case NO_RAND_PART_B_STATE: + SetupNoRandPartB(); + break; + case NO_RAND_PART_C_STATE: + SetupNoRandPartC(); + break; + default: + break; + } + return retChar; + } + } + + private void Initialize() { + char magic3, magic4; + magic3 = BsGetUChar(); + magic4 = BsGetUChar(); + if (magic3 != 'B' && magic4 != 'Z') + { + throw new IOException("Not a BZIP2 marked stream"); + } + magic3 = BsGetUChar(); + magic4 = BsGetUChar(); + if (magic3 != 'h' || magic4 < '1' || magic4 > '9') { + BsFinishedWithStream(); + streamEnd = true; + return; + } + + SetDecompressStructureSizes(magic4 - '0'); + computedCombinedCRC = 0; + } + + private void InitBlock() { + char magic1, magic2, magic3, magic4; + char magic5, magic6; + magic1 = BsGetUChar(); + magic2 = BsGetUChar(); + magic3 = BsGetUChar(); + magic4 = BsGetUChar(); + magic5 = BsGetUChar(); + magic6 = BsGetUChar(); + if (magic1 == 0x17 && magic2 == 0x72 && magic3 == 0x45 + && magic4 == 0x38 && magic5 == 0x50 && magic6 == 0x90) { + Complete(); + return; + } + + if (magic1 != 0x31 || magic2 != 0x41 || magic3 != 0x59 + || magic4 != 0x26 || magic5 != 0x53 || magic6 != 0x59) { + BadBlockHeader(); + streamEnd = true; + return; + } + + storedBlockCRC = BsGetInt32(); + + if (BsR(1) == 1) { + blockRandomised = true; + } else { + blockRandomised = false; + } + + // currBlockNo++; + GetAndMoveToFrontDecode(); + + mCrc.InitialiseCRC(); + currentState = START_BLOCK_STATE; + } + + private void EndBlock() { + computedBlockCRC = mCrc.GetFinalCRC(); + /* A bad CRC is considered a fatal error. */ + if (storedBlockCRC != computedBlockCRC) { + CrcError(); + } + + computedCombinedCRC = (computedCombinedCRC << 1) + | (int)(((uint)computedCombinedCRC) >> 31); + computedCombinedCRC ^= computedBlockCRC; + } + + private void Complete() { + storedCombinedCRC = BsGetInt32(); + if (storedCombinedCRC != computedCombinedCRC) { + CrcError(); + } + + BsFinishedWithStream(); + streamEnd = true; + } + + private static void BlockOverrun() { + Cadvise(); + } + + private static void BadBlockHeader() { + Cadvise(); + } + + private static void CrcError() { + Cadvise(); + } + + private void BsFinishedWithStream() { + try { + if (this.bsStream != null) { + this.bsStream.Close(); + this.bsStream = null; + } + } catch { + //ignore + } + } + + private void BsSetStream(Stream f) { + bsStream = f; + bsLive = 0; + bsBuff = 0; + } + + private int BsR(int n) { + int v; + while (bsLive < n) { + int zzi; + char thech = '\0'; + try { + thech = (char) bsStream.ReadByte(); + } catch (IOException) { + CompressedStreamEOF(); + } + if (thech == '\uffff') { + CompressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + + v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1); + bsLive -= n; + return v; + } + + private char BsGetUChar() { + return (char) BsR(8); + } + + private int BsGetint() { + int u = 0; + u = (u << 8) | BsR(8); + u = (u << 8) | BsR(8); + u = (u << 8) | BsR(8); + u = (u << 8) | BsR(8); + return u; + } + + private int BsGetIntVS(int numBits) { + return (int) BsR(numBits); + } + + private int BsGetInt32() { + return (int) BsGetint(); + } + + private void HbCreateDecodeTables(int[] limit, int[] basev, + int[] perm, char[] length, + int minLen, int maxLen, int alphaSize) { + int pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) { + for (j = 0; j < alphaSize; j++) { + if (length[j] == i) { + perm[pp] = j; + pp++; + } + } + } + + for (i = 0; i < BZip2Constants.MAX_CODE_LEN; i++) { + basev[i] = 0; + } + for (i = 0; i < alphaSize; i++) { + basev[length[i] + 1]++; + } + + for (i = 1; i < BZip2Constants.MAX_CODE_LEN; i++) { + basev[i] += basev[i - 1]; + } + + for (i = 0; i < BZip2Constants.MAX_CODE_LEN; i++) { + limit[i] = 0; + } + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (basev[i + 1] - basev[i]); + limit[i] = vec - 1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) { + basev[i] = ((limit[i - 1] + 1) << 1) - basev[i]; + } + } + + private void RecvDecodingTables() { + char[][] len = InitCharArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); + int i, j, t, nGroups, nSelectors, alphaSize; + int minLen, maxLen; + bool[] inUse16 = new bool[16]; + + /* Receive the mapping table */ + for (i = 0; i < 16; i++) { + if (BsR(1) == 1) { + inUse16[i] = true; + } else { + inUse16[i] = false; + } + } + + for (i = 0; i < 256; i++) { + inUse[i] = false; + } + + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + for (j = 0; j < 16; j++) { + if (BsR(1) == 1) { + inUse[i * 16 + j] = true; + } + } + } + } + + MakeMaps(); + alphaSize = nInUse + 2; + + /* Now the selectors */ + nGroups = BsR(3); + nSelectors = BsR(15); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (BsR(1) == 1) { + j++; + } + selectorMtf[i] = (char) j; + } + + /* Undo the MTF values for the selectors. */ + { + char[] pos = new char[BZip2Constants.N_GROUPS]; + char tmp, v; + for (v = '\0'; v < nGroups; v++) { + pos[v] = v; + } + + for (i = 0; i < nSelectors; i++) { + v = selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { + pos[v] = pos[v - 1]; + v--; + } + pos[0] = tmp; + selector[i] = tmp; + } + } + + /* Now the coding tables */ + for (t = 0; t < nGroups; t++) { + int curr = BsR(5); + for (i = 0; i < alphaSize; i++) { + while (BsR(1) == 1) { + if (BsR(1) == 0) { + curr++; + } else { + curr--; + } + } + len[t][i] = (char) curr; + } + } + + /* Create the Huffman decoding tables */ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (len[t][i] > maxLen) { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) { + minLen = len[t][i]; + } + } + HbCreateDecodeTables(limit[t], basev[t], perm[t], len[t], minLen, + maxLen, alphaSize); + minLens[t] = minLen; + } + } + + private void GetAndMoveToFrontDecode() { + char[] yy = new char[256]; + int i, j, nextSym, limitLast; + int EOB, groupNo, groupPos; + + limitLast = BZip2Constants.baseBlockSize * blockSize100k; + origPtr = BsGetIntVS(24); + + RecvDecodingTables(); + EOB = nInUse + 1; + groupNo = -1; + groupPos = 0; + + /* + Setting up the unzftab entries here is not strictly + necessary, but it does save having to do it later + in a separate pass, and so saves a block's worth of + cache misses. + */ + for (i = 0; i <= 255; i++) { + unzftab[i] = 0; + } + + for (i = 0; i <= 255; i++) { + yy[i] = (char) i; + } + + last = -1; + + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = BZip2Constants.G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = BsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = '\0'; + try { + thech = (char) bsStream.ReadByte(); + } catch (IOException) { + CompressedStreamEOF(); + } + if (thech == '\uffff') { + CompressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - basev[zt][zn]]; + } + + while (true) { + + if (nextSym == EOB) { + break; + } + + if (nextSym == BZip2Constants.RUNA || nextSym == BZip2Constants.RUNB) { + char ch; + int s = -1; + int N = 1; + do { + if (nextSym == BZip2Constants.RUNA) { + s = s + (0 + 1) * N; + } else if (nextSym == BZip2Constants.RUNB) { + s = s + (1 + 1) * N; + } + N = N * 2; + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = BZip2Constants.G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = BsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = '\0'; + try { + thech = (char) bsStream.ReadByte(); + } catch (IOException) { + CompressedStreamEOF(); + } + if (thech == '\uffff') { + CompressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - basev[zt][zn]]; + } + } while (nextSym == BZip2Constants.RUNA || nextSym == BZip2Constants.RUNB); + + s++; + ch = seqToUnseq[yy[0]]; + unzftab[ch] += s; + + while (s > 0) { + last++; + ll8[last] = ch; + s--; + } + + if (last >= limitLast) { + BlockOverrun(); + } + continue; + } else { + char tmp; + last++; + if (last >= limitLast) { + BlockOverrun(); + } + + tmp = yy[nextSym - 1]; + unzftab[seqToUnseq[tmp]]++; + ll8[last] = seqToUnseq[tmp]; + + /* + This loop is hammered during decompression, + hence the unrolling. + + for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1]; + */ + + j = nextSym - 1; + for (; j > 3; j -= 4) { + yy[j] = yy[j - 1]; + yy[j - 1] = yy[j - 2]; + yy[j - 2] = yy[j - 3]; + yy[j - 3] = yy[j - 4]; + } + for (; j > 0; j--) { + yy[j] = yy[j - 1]; + } + + yy[0] = tmp; + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = BZip2Constants.G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = BsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = '\0'; + try { + thech = (char) bsStream.ReadByte(); + } catch (IOException) { + CompressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - basev[zt][zn]]; + } + continue; + } + } + } + + private void SetupBlock() { + int[] cftab = new int[257]; + char ch; + + cftab[0] = 0; + for (i = 1; i <= 256; i++) { + cftab[i] = unzftab[i - 1]; + } + for (i = 1; i <= 256; i++) { + cftab[i] += cftab[i - 1]; + } + + for (i = 0; i <= last; i++) { + ch = (char) ll8[i]; + tt[cftab[ch]] = i; + cftab[ch]++; + } + cftab = null; + + tPos = tt[origPtr]; + + count = 0; + i2 = 0; + ch2 = 256; /* not a char and not EOF */ + + if (blockRandomised) { + rNToGo = 0; + rTPos = 0; + SetupRandPartA(); + } else { + SetupNoRandPartA(); + } + } + + private void SetupRandPartA() { + if (i2 <= last) { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) { + rNToGo = BZip2Constants.rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + ch2 ^= (int) ((rNToGo == 1) ? 1 : 0); + i2++; + + currentChar = ch2; + currentState = RAND_PART_B_STATE; + mCrc.UpdateCRC(ch2); + } else { + EndBlock(); + InitBlock(); + SetupBlock(); + } + } + + private void SetupNoRandPartA() { + if (i2 <= last) { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + i2++; + + currentChar = ch2; + currentState = NO_RAND_PART_B_STATE; + mCrc.UpdateCRC(ch2); + } else { + EndBlock(); + InitBlock(); + SetupBlock(); + } + } + + private void SetupRandPartB() { + if (ch2 != chPrev) { + currentState = RAND_PART_A_STATE; + count = 1; + SetupRandPartA(); + } else { + count++; + if (count >= 4) { + z = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) { + rNToGo = BZip2Constants.rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + z ^= (char)((rNToGo == 1) ? 1 : 0); + j2 = 0; + currentState = RAND_PART_C_STATE; + SetupRandPartC(); + } else { + currentState = RAND_PART_A_STATE; + SetupRandPartA(); + } + } + } + + private void SetupRandPartC() { + if (j2 < (int) z) { + currentChar = ch2; + mCrc.UpdateCRC(ch2); + j2++; + } else { + currentState = RAND_PART_A_STATE; + i2++; + count = 0; + SetupRandPartA(); + } + } + + private void SetupNoRandPartB() { + if (ch2 != chPrev) { + currentState = NO_RAND_PART_A_STATE; + count = 1; + SetupNoRandPartA(); + } else { + count++; + if (count >= 4) { + z = ll8[tPos]; + tPos = tt[tPos]; + currentState = NO_RAND_PART_C_STATE; + j2 = 0; + SetupNoRandPartC(); + } else { + currentState = NO_RAND_PART_A_STATE; + SetupNoRandPartA(); + } + } + } + + private void SetupNoRandPartC() { + if (j2 < (int) z) { + currentChar = ch2; + mCrc.UpdateCRC(ch2); + j2++; + } else { + currentState = NO_RAND_PART_A_STATE; + i2++; + count = 0; + SetupNoRandPartA(); + } + } + + private void SetDecompressStructureSizes(int newSize100k) { + if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k + && blockSize100k <= 9)) { + // throw new IOException("Invalid block size"); + } + + blockSize100k = newSize100k; + + if (newSize100k == 0) { + return; + } + + int n = BZip2Constants.baseBlockSize * newSize100k; + ll8 = new char[n]; + tt = new int[n]; + } + + public override void Flush() { + } + + public override int Read(byte[] buffer, int offset, int count) { + int c = -1; + int k; + for (k = 0; k < count; ++k) { + c = ReadByte(); + if (c == -1) + break; + buffer[k + offset] = (byte)c; + } + return k; + } + + public override long Seek(long offset, SeekOrigin origin) { + return 0; + } + + public override void SetLength(long value) { + } + + public override void Write(byte[] buffer, int offset, int count) { + } + + public override bool CanRead { + get { + return true; + } + } + + public override bool CanSeek { + get { + return false; + } + } + + public override bool CanWrite { + get { + return false; + } + } + + public override long Length { + get { + return 0; + } + } + + public override long Position { + get { + return 0; + } + set { + } + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/bzip2/CBZip2OutputStream.cs b/src/core/srcbc/util/bzip2/CBZip2OutputStream.cs new file mode 100644 index 0000000..71bbbbc --- /dev/null +++ b/src/core/srcbc/util/bzip2/CBZip2OutputStream.cs @@ -0,0 +1,1731 @@ +using System; +using System.IO; +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Ant" and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * to whom the Ant project is very grateful for his + * great code. + */ + +namespace Org.BouncyCastle.Apache.Bzip2 +{ + /** + * An output stream that compresses into the BZip2 format (with the file + * header chars) into another stream. + * + * @author Keiron Liddle + * + * TODO: Update to BZip2 1.0.1 + * NB: note this class has been modified to add a leading BZ to the + * start of the BZIP2 stream to make it compatible with other PGP programs. + */ + public class CBZip2OutputStream : Stream + { + protected const int SETMASK = (1 << 21); + protected const int CLEARMASK = (~SETMASK); + protected const int GREATER_ICOST = 15; + protected const int LESSER_ICOST = 0; + protected const int SMALL_THRESH = 20; + protected const int DEPTH_THRESH = 10; + + /* + If you are ever unlucky/improbable enough + to get a stack overflow whilst sorting, + increase the following constant and try + again. In practice I have never seen the + stack go above 27 elems, so the following + limit seems very generous. + */ + protected const int QSORT_STACK_SIZE = 1000; + private bool finished; + + private static void Panic() { + //System.out.Println("panic"); + //throw new CError(); + } + + private void MakeMaps() { + int i; + nInUse = 0; + for (i = 0; i < 256; i++) { + if (inUse[i]) { + seqToUnseq[nInUse] = (char) i; + unseqToSeq[i] = (char) nInUse; + nInUse++; + } + } + } + + protected static void HbMakeCodeLengths(char[] len, int[] freq, + int alphaSize, int maxLen) { + /* + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + */ + int nNodes, nHeap, n1, n2, i, j, k; + bool tooLong; + + int[] heap = new int[BZip2Constants.MAX_ALPHA_SIZE + 2]; + int[] weight = new int[BZip2Constants.MAX_ALPHA_SIZE * 2]; + int[] parent = new int[BZip2Constants.MAX_ALPHA_SIZE * 2]; + + for (i = 0; i < alphaSize; i++) { + weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + } + + while (true) { + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + { + int zz, tmp; + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + } + if (!(nHeap < (BZip2Constants.MAX_ALPHA_SIZE + 2))) { + Panic(); + } + + while (nHeap > 1) { + n1 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + { + int zz = 0, yy = 0, tmp = 0; + zz = 1; + tmp = heap[zz]; + while (true) { + yy = zz << 1; + if (yy > nHeap) { + break; + } + if (yy < nHeap + && weight[heap[yy + 1]] < weight[heap[yy]]) { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) { + break; + } + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; + } + n2 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + { + int zz = 0, yy = 0, tmp = 0; + zz = 1; + tmp = heap[zz]; + while (true) { + yy = zz << 1; + if (yy > nHeap) { + break; + } + if (yy < nHeap + && weight[heap[yy + 1]] < weight[heap[yy]]) { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) { + break; + } + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; + } + nNodes++; + parent[n1] = parent[n2] = nNodes; + + weight[nNodes] = (int)((uint)((weight[n1] & 0xffffff00) + + (weight[n2] & 0xffffff00)) + | (uint)(1 + (((weight[n1] & 0x000000ff) > + (weight[n2] & 0x000000ff)) ? + (weight[n1] & 0x000000ff) : + (weight[n2] & 0x000000ff)))); + + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + { + int zz = 0, tmp = 0; + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + } + if (!(nNodes < (BZip2Constants.MAX_ALPHA_SIZE * 2))) { + Panic(); + } + + tooLong = false; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { + k = parent[k]; + j++; + } + len[i - 1] = (char) j; + if (j > maxLen) { + tooLong = true; + } + } + + if (!tooLong) { + break; + } + + for (i = 1; i < alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } + } + + /* + index of the last char in the block, so + the block size == last + 1. + */ + int last; + + /* + index in zptr[] of original string after sorting. + */ + int origPtr; + + /* + always: in the range 0 .. 9. + The current block size is 100000 * this number. + */ + int blockSize100k; + + bool blockRandomised; + + int bytesOut; + int bsBuff; + int bsLive; + CRC mCrc = new CRC(); + + private bool[] inUse = new bool[256]; + private int nInUse; + + private char[] seqToUnseq = new char[256]; + private char[] unseqToSeq = new char[256]; + + private char[] selector = new char[BZip2Constants.MAX_SELECTORS]; + private char[] selectorMtf = new char[BZip2Constants.MAX_SELECTORS]; + + private char[] block; + private int[] quadrant; + private int[] zptr; + private short[] szptr; + private int[] ftab; + + private int nMTF; + + private int[] mtfFreq = new int[BZip2Constants.MAX_ALPHA_SIZE]; + + /* + * Used when sorting. If too many long comparisons + * happen, we stop sorting, randomise the block + * slightly, and try again. + */ + private int workFactor; + private int workDone; + private int workLimit; + private bool firstAttempt; + private int nBlocksRandomised; + + private int currentChar = -1; + private int runLength = 0; + + public CBZip2OutputStream(Stream inStream) : this(inStream, 9) { + } + + public CBZip2OutputStream(Stream inStream, int inBlockSize) + { + block = null; + quadrant = null; + zptr = null; + ftab = null; + + inStream.WriteByte((byte)'B'); + inStream.WriteByte((byte)'Z'); + + BsSetStream(inStream); + + workFactor = 50; + if (inBlockSize > 9) { + inBlockSize = 9; + } + if (inBlockSize < 1) { + inBlockSize = 1; + } + blockSize100k = inBlockSize; + AllocateCompressStructures(); + Initialize(); + InitBlock(); + } + + /** + * + * modified by Oliver Merkel, 010128 + * + */ + public override void WriteByte(byte bv) { + int b = (256 + bv) % 256; + if (currentChar != -1) { + if (currentChar == b) { + runLength++; + if (runLength > 254) { + WriteRun(); + currentChar = -1; + runLength = 0; + } + } else { + WriteRun(); + runLength = 1; + currentChar = b; + } + } else { + currentChar = b; + runLength++; + } + } + + private void WriteRun() { + if (last < allowableBlockSize) { + inUse[currentChar] = true; + for (int i = 0; i < runLength; i++) { + mCrc.UpdateCRC((char) currentChar); + } + switch (runLength) { + case 1: + last++; + block[last + 1] = (char) currentChar; + break; + case 2: + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + break; + case 3: + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + break; + default: + inUse[runLength - 4] = true; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) (runLength - 4); + break; + } + } else { + EndBlock(); + InitBlock(); + WriteRun(); + } + } + + bool closed = false; + + protected void Finalize() { + Close(); + } + + public override void Close() { + if (closed) { + return; + } + + Finish(); + + closed = true; + base.Close(); + bsStream.Close(); + } + + public void Finish() { + if (finished) { + return; + } + + if (runLength > 0) { + WriteRun(); + } + currentChar = -1; + EndBlock(); + EndCompression(); + finished = true; + Flush(); + } + + public override void Flush() { + bsStream.Flush(); + } + + private int blockCRC, combinedCRC; + + private void Initialize() { + bytesOut = 0; + nBlocksRandomised = 0; + + /* Write `magic' bytes h indicating file-format == huffmanised, + followed by a digit indicating blockSize100k. + */ + BsPutUChar('h'); + BsPutUChar('0' + blockSize100k); + + combinedCRC = 0; + } + + private int allowableBlockSize; + + private void InitBlock() { + // blockNo++; + mCrc.InitialiseCRC(); + last = -1; + // ch = 0; + + for (int i = 0; i < 256; i++) { + inUse[i] = false; + } + + /* 20 is just a paranoia constant */ + allowableBlockSize = BZip2Constants.baseBlockSize * blockSize100k - 20; + } + + private void EndBlock() { + blockCRC = mCrc.GetFinalCRC(); + combinedCRC = (combinedCRC << 1) | (int)(((uint)combinedCRC) >> 31); + combinedCRC ^= blockCRC; + + /* sort the block and establish posn of original string */ + DoReversibleTransformation(); + + /* + A 6-byte block header, the value chosen arbitrarily + as 0x314159265359 :-). A 32 bit value does not really + give a strong enough guarantee that the value will not + appear by chance in the compressed datastream. Worst-case + probability of this event, for a 900k block, is about + 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits. + For a compressed file of size 100Gb -- about 100000 blocks -- + only a 48-bit marker will do. NB: normal compression/ + decompression do *not* rely on these statistical properties. + They are only important when trying to recover blocks from + damaged files. + */ + BsPutUChar(0x31); + BsPutUChar(0x41); + BsPutUChar(0x59); + BsPutUChar(0x26); + BsPutUChar(0x53); + BsPutUChar(0x59); + + /* Now the block's CRC, so it is in a known place. */ + BsPutint(blockCRC); + + /* Now a single bit indicating randomisation. */ + if (blockRandomised) { + BsW(1, 1); + nBlocksRandomised++; + } else { + BsW(1, 0); + } + + /* Finally, block's contents proper. */ + MoveToFrontCodeAndSend(); + } + + private void EndCompression() { + /* + Now another magic 48-bit number, 0x177245385090, to + indicate the end of the last block. (Sqrt(pi), if + you want to know. I did want to use e, but it contains + too much repetition -- 27 18 28 18 28 46 -- for me + to feel statistically comfortable. Call me paranoid.) + */ + BsPutUChar(0x17); + BsPutUChar(0x72); + BsPutUChar(0x45); + BsPutUChar(0x38); + BsPutUChar(0x50); + BsPutUChar(0x90); + + BsPutint(combinedCRC); + + BsFinishedWithStream(); + } + + private void HbAssignCodes(int[] code, char[] length, int minLen, + int maxLen, int alphaSize) { + int n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) { + if (length[i] == n) { + code[i] = vec; + vec++; + } + }; + vec <<= 1; + } + } + + private void BsSetStream(Stream f) { + bsStream = f; + bsLive = 0; + bsBuff = 0; + bytesOut = 0; + } + + private void BsFinishedWithStream() { + while (bsLive > 0) { + int ch = (bsBuff >> 24); + try { + bsStream.WriteByte((byte)ch); // write 8-bit + } catch (IOException e) { + throw e; + } + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + } + + private void BsW(int n, int v) { + while (bsLive >= 8) { + int ch = (bsBuff >> 24); + try { + bsStream.WriteByte((byte)ch); // write 8-bit + } catch (IOException e) { + throw e; + } + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + bsBuff |= (v << (32 - bsLive - n)); + bsLive += n; + } + + private void BsPutUChar(int c) { + BsW(8, c); + } + + private void BsPutint(int u) { + BsW(8, (u >> 24) & 0xff); + BsW(8, (u >> 16) & 0xff); + BsW(8, (u >> 8) & 0xff); + BsW(8, u & 0xff); + } + + private void BsPutIntVS(int numBits, int c) { + BsW(numBits, c); + } + + private void SendMTFValues() { + char[][] len = CBZip2InputStream.InitCharArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); + + int v, t, i, j, gs, ge, totc, bt, bc, iter; + int nSelectors = 0, alphaSize, minLen, maxLen, selCtr; + int nGroups, nBytes; + + alphaSize = nInUse + 2; + for (t = 0; t < BZip2Constants.N_GROUPS; t++) { + for (v = 0; v < alphaSize; v++) { + len[t][v] = (char) GREATER_ICOST; + } + } + + /* Decide how many coding tables to use */ + if (nMTF <= 0) { + Panic(); + } + + if (nMTF < 200) { + nGroups = 2; + } else if (nMTF < 600) { + nGroups = 3; + } else if (nMTF < 1200) { + nGroups = 4; + } else if (nMTF < 2400) { + nGroups = 5; + } else { + nGroups = 6; + } + + /* Generate an initial set of coding tables */ { + int nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs - 1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize - 1) { + ge++; + aFreq += mtfFreq[ge]; + } + + if (ge > gs && nPart != nGroups && nPart != 1 + && ((nGroups - nPart) % 2 == 1)) { + aFreq -= mtfFreq[ge]; + ge--; + } + + for (v = 0; v < alphaSize; v++) { + if (v >= gs && v <= ge) { + len[nPart - 1][v] = (char) LESSER_ICOST; + } else { + len[nPart - 1][v] = (char) GREATER_ICOST; + } + } + + nPart--; + gs = ge + 1; + remF -= aFreq; + } + } + + int[][] rfreq = CBZip2InputStream.InitIntArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); + int[] fave = new int[BZip2Constants.N_GROUPS]; + short[] cost = new short[BZip2Constants.N_GROUPS]; + /* + Iterate up to N_ITERS times to improve the tables. + */ + for (iter = 0; iter < BZip2Constants.N_ITERS; iter++) { + for (t = 0; t < nGroups; t++) { + fave[t] = 0; + } + + for (t = 0; t < nGroups; t++) { + for (v = 0; v < alphaSize; v++) { + rfreq[t][v] = 0; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (true) { + + /* Set group start & end marks. */ + if (gs >= nMTF) { + break; + } + ge = gs + BZip2Constants.G_SIZE - 1; + if (ge >= nMTF) { + ge = nMTF - 1; + } + + /* + Calculate the cost of this group as coded + by each of the coding tables. + */ + for (t = 0; t < nGroups; t++) { + cost[t] = 0; + } + + if (nGroups == 6) { + short cost0, cost1, cost2, cost3, cost4, cost5; + cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0; + for (i = gs; i <= ge; i++) { + short icv = szptr[i]; + cost0 += (short)len[0][icv]; + cost1 += (short)len[1][icv]; + cost2 += (short)len[2][icv]; + cost3 += (short)len[3][icv]; + cost4 += (short)len[4][icv]; + cost5 += (short)len[5][icv]; + } + cost[0] = cost0; + cost[1] = cost1; + cost[2] = cost2; + cost[3] = cost3; + cost[4] = cost4; + cost[5] = cost5; + } else { + for (i = gs; i <= ge; i++) { + short icv = szptr[i]; + for (t = 0; t < nGroups; t++) { + cost[t] += (short)len[t][icv]; + } + } + } + + /* + Find the coding table which is best for this group, + and record its identity in the selector table. + */ + bc = 999999999; + bt = -1; + for (t = 0; t < nGroups; t++) { + if (cost[t] < bc) { + bc = cost[t]; + bt = t; + } + }; + totc += bc; + fave[bt]++; + selector[nSelectors] = (char) bt; + nSelectors++; + + /* + Increment the symbol frequencies for the selected table. + */ + for (i = gs; i <= ge; i++) { + rfreq[bt][szptr[i]]++; + } + + gs = ge + 1; + } + + /* + Recompute the tables based on the accumulated frequencies. + */ + for (t = 0; t < nGroups; t++) { + HbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20); + } + } + + rfreq = null; + fave = null; + cost = null; + + if (!(nGroups < 8)) { + Panic(); + } + if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZip2Constants.G_SIZE)))) { + Panic(); + } + + + /* Compute MTF values for the selectors. */ + { + char[] pos = new char[BZip2Constants.N_GROUPS]; + char ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) { + pos[i] = (char) i; + } + for (i = 0; i < nSelectors; i++) { + ll_i = selector[i]; + j = 0; + tmp = pos[j]; + while (ll_i != tmp) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + } + pos[0] = tmp; + selectorMtf[i] = (char) j; + } + } + + int[][] code = CBZip2InputStream.InitIntArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); + + /* Assign actual codes for the tables. */ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (len[t][i] > maxLen) { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) { + minLen = len[t][i]; + } + } + if (maxLen > 20) { + Panic(); + } + if (minLen < 1) { + Panic(); + } + HbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize); + } + + /* Transmit the mapping table. */ + { + bool[] inUse16 = new bool[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = false; + for (j = 0; j < 16; j++) { + if (inUse[i * 16 + j]) { + inUse16[i] = true; + } + } + } + + nBytes = bytesOut; + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + BsW(1, 1); + } else { + BsW(1, 0); + } + } + + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + for (j = 0; j < 16; j++) { + if (inUse[i * 16 + j]) { + BsW(1, 1); + } else { + BsW(1, 0); + } + } + } + } + + } + + /* Now the selectors. */ + nBytes = bytesOut; + BsW(3, nGroups); + BsW(15, nSelectors); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < selectorMtf[i]; j++) { + BsW(1, 1); + } + BsW(1, 0); + } + + /* Now the coding tables. */ + nBytes = bytesOut; + + for (t = 0; t < nGroups; t++) { + int curr = len[t][0]; + BsW(5, curr); + for (i = 0; i < alphaSize; i++) { + while (curr < len[t][i]) { + BsW(2, 2); + curr++; /* 10 */ + } + while (curr > len[t][i]) { + BsW(2, 3); + curr--; /* 11 */ + } + BsW(1, 0); + } + } + + /* And finally, the block data proper */ + nBytes = bytesOut; + selCtr = 0; + gs = 0; + while (true) { + if (gs >= nMTF) { + break; + } + ge = gs + BZip2Constants.G_SIZE - 1; + if (ge >= nMTF) { + ge = nMTF - 1; + } + for (i = gs; i <= ge; i++) { + BsW(len[selector[selCtr]][szptr[i]], + code[selector[selCtr]][szptr[i]]); + } + + gs = ge + 1; + selCtr++; + } + if (!(selCtr == nSelectors)) { + Panic(); + } + } + + private void MoveToFrontCodeAndSend() { + BsPutIntVS(24, origPtr); + GenerateMTFValues(); + SendMTFValues(); + } + + private Stream bsStream; + + private void SimpleSort(int lo, int hi, int d) { + int i, j, h, bigN, hp; + int v; + + bigN = hi - lo + 1; + if (bigN < 2) { + return; + } + + hp = 0; + while (incs[hp] < bigN) { + hp++; + } + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (true) { + /* copy 1 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + /* copy 2 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + /* copy 3 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + if (workDone > workLimit && firstAttempt) { + return; + } + } + } + } + + private void Vswap(int p1, int p2, int n) { + int temp = 0; + while (n > 0) { + temp = zptr[p1]; + zptr[p1] = zptr[p2]; + zptr[p2] = temp; + p1++; + p2++; + n--; + } + } + + private char Med3(char a, char b, char c) { + char t; + if (a > b) { + t = a; + a = b; + b = t; + } + if (b > c) { + t = b; + b = c; + c = t; + } + if (a > b) { + b = a; + } + return b; + } + + internal class StackElem { + internal int ll; + internal int hh; + internal int dd; + } + + private void QSort3(int loSt, int hiSt, int dSt) { + int unLo, unHi, ltLo, gtHi, med, n, m; + int sp, lo, hi, d; + StackElem[] stack = new StackElem[QSORT_STACK_SIZE]; + for (int count = 0; count < QSORT_STACK_SIZE; count++) { + stack[count] = new StackElem(); + } + + sp = 0; + + stack[sp].ll = loSt; + stack[sp].hh = hiSt; + stack[sp].dd = dSt; + sp++; + + while (sp > 0) { + if (sp >= QSORT_STACK_SIZE) { + Panic(); + } + + sp--; + lo = stack[sp].ll; + hi = stack[sp].hh; + d = stack[sp].dd; + + if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) { + SimpleSort(lo, hi, d); + if (workDone > workLimit && firstAttempt) { + return; + } + continue; + } + + med = Med3(block[zptr[lo] + d + 1], + block[zptr[hi ] + d + 1], + block[zptr[(lo + hi) >> 1] + d + 1]); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (true) { + while (true) { + if (unLo > unHi) { + break; + } + n = ((int) block[zptr[unLo] + d + 1]) - med; + if (n == 0) { + int temp = 0; + temp = zptr[unLo]; + zptr[unLo] = zptr[ltLo]; + zptr[ltLo] = temp; + ltLo++; + unLo++; + continue; + }; + if (n > 0) { + break; + } + unLo++; + } + while (true) { + if (unLo > unHi) { + break; + } + n = ((int) block[zptr[unHi] + d + 1]) - med; + if (n == 0) { + int temp = 0; + temp = zptr[unHi]; + zptr[unHi] = zptr[gtHi]; + zptr[gtHi] = temp; + gtHi--; + unHi--; + continue; + }; + if (n < 0) { + break; + } + unHi--; + } + if (unLo > unHi) { + break; + } + int tempx = zptr[unLo]; + zptr[unLo] = zptr[unHi]; + zptr[unHi] = tempx; + unLo++; + unHi--; + } + + if (gtHi < ltLo) { + stack[sp].ll = lo; + stack[sp].hh = hi; + stack[sp].dd = d + 1; + sp++; + continue; + } + + n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo); + Vswap(lo, unLo - n, n); + m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi); + Vswap(unLo, hi - m + 1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + stack[sp].ll = lo; + stack[sp].hh = n; + stack[sp].dd = d; + sp++; + + stack[sp].ll = n + 1; + stack[sp].hh = m - 1; + stack[sp].dd = d + 1; + sp++; + + stack[sp].ll = m; + stack[sp].hh = hi; + stack[sp].dd = d; + sp++; + } + } + + private void MainSort() { + int i, j, ss, sb; + int[] runningOrder = new int[256]; + int[] copy = new int[256]; + bool[] bigDone = new bool[256]; + int c1, c2; + int numQSorted; + + /* + In the various block-sized structures, live data runs + from 0 to last+NUM_OVERSHOOT_BYTES inclusive. First, + set up the overshoot area for block. + */ + + // if (verbosity >= 4) fprintf ( stderr, " sort initialise ...\n" ); + for (i = 0; i < BZip2Constants.NUM_OVERSHOOT_BYTES; i++) { + block[last + i + 2] = block[(i % (last + 1)) + 1]; + } + for (i = 0; i <= last + BZip2Constants.NUM_OVERSHOOT_BYTES; i++) { + quadrant[i] = 0; + } + + block[0] = (char) (block[last + 1]); + + if (last < 4000) { + /* + Use SimpleSort(), since the full sorting mechanism + has quite a large constant overhead. + */ + for (i = 0; i <= last; i++) { + zptr[i] = i; + } + firstAttempt = false; + workDone = workLimit = 0; + SimpleSort(0, last, 0); + } else { + numQSorted = 0; + for (i = 0; i <= 255; i++) { + bigDone[i] = false; + } + + for (i = 0; i <= 65536; i++) { + ftab[i] = 0; + } + + c1 = block[0]; + for (i = 0; i <= last; i++) { + c2 = block[i + 1]; + ftab[(c1 << 8) + c2]++; + c1 = c2; + } + + for (i = 1; i <= 65536; i++) { + ftab[i] += ftab[i - 1]; + } + + c1 = block[1]; + for (i = 0; i < last; i++) { + c2 = block[i + 2]; + j = (c1 << 8) + c2; + c1 = c2; + ftab[j]--; + zptr[ftab[j]] = i; + } + + j = ((block[last + 1]) << 8) + (block[1]); + ftab[j]--; + zptr[ftab[j]] = last; + + /* + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + */ + + for (i = 0; i <= 255; i++) { + runningOrder[i] = i; + } + + { + int vv; + int h = 1; + do { + h = 3 * h + 1; + } + while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ((ftab[((runningOrder[j - h]) + 1) << 8] + - ftab[(runningOrder[j - h]) << 8]) > + (ftab[((vv) + 1) << 8] - ftab[(vv) << 8])) { + runningOrder[j] = runningOrder[j - h]; + j = j - h; + if (j <= (h - 1)) { + break; + } + } + runningOrder[j] = vv; + } + } while (h != 1); + } + + /* + The main sorting loop. + */ + for (i = 0; i <= 255; i++) { + + /* + Process big buckets, starting with the least full. + */ + ss = runningOrder[i]; + + /* + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j]. Hopefully + previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + */ + for (j = 0; j <= 255; j++) { + sb = (ss << 8) + j; + if (!((ftab[sb] & SETMASK) == SETMASK)) { + int lo = ftab[sb] & CLEARMASK; + int hi = (ftab[sb + 1] & CLEARMASK) - 1; + if (hi > lo) { + QSort3(lo, hi, 2); + numQSorted += (hi - lo + 1); + if (workDone > workLimit && firstAttempt) { + return; + } + } + ftab[sb] |= SETMASK; + } + } + + /* + The ss big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + */ + bigDone[ss] = true; + + if (i < 255) { + int bbStart = ftab[ss << 8] & CLEARMASK; + int bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart; + int shifts = 0; + + while ((bbSize >> shifts) > 65534) { + shifts++; + } + + for (j = 0; j < bbSize; j++) { + int a2update = zptr[bbStart + j]; + int qVal = (j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZip2Constants.NUM_OVERSHOOT_BYTES) { + quadrant[a2update + last + 1] = qVal; + } + } + + if (!(((bbSize - 1) >> shifts) <= 65535)) { + Panic(); + } + } + + /* + Now scan this big bucket so as to synthesise the + sorted order for small buckets [t, ss] for all t != ss. + */ + for (j = 0; j <= 255; j++) { + copy[j] = ftab[(j << 8) + ss] & CLEARMASK; + } + + for (j = ftab[ss << 8] & CLEARMASK; + j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) { + c1 = block[zptr[j]]; + if (!bigDone[c1]) { + zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1; + copy[c1]++; + } + } + + for (j = 0; j <= 255; j++) { + ftab[(j << 8) + ss] |= SETMASK; + } + } + } + } + + private void RandomiseBlock() { + int i; + int rNToGo = 0; + int rTPos = 0; + for (i = 0; i < 256; i++) { + inUse[i] = false; + } + + for (i = 0; i <= last; i++) { + if (rNToGo == 0) { + rNToGo = (char) BZip2Constants.rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + block[i + 1] ^= (char)((rNToGo == 1) ? 1 : 0); + // handle 16 bit signed numbers + block[i + 1] &= (char)0xFF; + + inUse[block[i + 1]] = true; + } + } + + private void DoReversibleTransformation() { + int i; + + workLimit = workFactor * last; + workDone = 0; + blockRandomised = false; + firstAttempt = true; + + MainSort(); + + if (workDone > workLimit && firstAttempt) { + RandomiseBlock(); + workLimit = workDone = 0; + blockRandomised = true; + firstAttempt = false; + MainSort(); + } + + origPtr = -1; + for (i = 0; i <= last; i++) { + if (zptr[i] == 0) { + origPtr = i; + break; + } + }; + + if (origPtr == -1) { + Panic(); + } + } + + private bool FullGtU(int i1, int i2) { + int k; + char c1, c2; + int s1, s2; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + k = last + 1; + + do { + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + if (i1 > last) { + i1 -= last; + i1--; + }; + if (i2 > last) { + i2 -= last; + i2--; + }; + + k -= 4; + workDone++; + } while (k >= 0); + + return false; + } + + /* + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. + */ + private int[] incs = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + + private void AllocateCompressStructures() { + int n = BZip2Constants.baseBlockSize * blockSize100k; + block = new char[(n + 1 + BZip2Constants.NUM_OVERSHOOT_BYTES)]; + quadrant = new int[(n + BZip2Constants.NUM_OVERSHOOT_BYTES)]; + zptr = new int[n]; + ftab = new int[65537]; + + if (block == null || quadrant == null || zptr == null + || ftab == null) { + //int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537; + //compressOutOfMemory ( totalDraw, n ); + } + + /* + The back end needs a place to store the MTF values + whilst it calculates the coding tables. We could + put them in the zptr array. However, these values + will fit in a short, so we overlay szptr at the + start of zptr, in the hope of reducing the number + of cache misses induced by the multiple traversals + of the MTF values when calculating coding tables. + Seems to improve compression speed by about 1%. + */ + // szptr = zptr; + + + szptr = new short[2 * n]; + } + + private void GenerateMTFValues() { + char[] yy = new char[256]; + int i, j; + char tmp; + char tmp2; + int zPend; + int wr; + int EOB; + + MakeMaps(); + EOB = nInUse + 1; + + for (i = 0; i <= EOB; i++) { + mtfFreq[i] = 0; + } + + wr = 0; + zPend = 0; + for (i = 0; i < nInUse; i++) { + yy[i] = (char) i; + } + + + for (i = 0; i <= last; i++) { + char ll_i; + + ll_i = unseqToSeq[block[zptr[i]]]; + + j = 0; + tmp = yy[j]; + while (ll_i != tmp) { + j++; + tmp2 = tmp; + tmp = yy[j]; + yy[j] = tmp2; + }; + yy[0] = tmp; + + if (j == 0) { + zPend++; + } else { + if (zPend > 0) { + zPend--; + while (true) { + switch (zPend % 2) { + case 0: + szptr[wr] = (short) BZip2Constants.RUNA; + wr++; + mtfFreq[BZip2Constants.RUNA]++; + break; + case 1: + szptr[wr] = (short) BZip2Constants.RUNB; + wr++; + mtfFreq[BZip2Constants.RUNB]++; + break; + }; + if (zPend < 2) { + break; + } + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + szptr[wr] = (short) (j + 1); + wr++; + mtfFreq[j + 1]++; + } + } + + if (zPend > 0) { + zPend--; + while (true) { + switch (zPend % 2) { + case 0: + szptr[wr] = (short) BZip2Constants.RUNA; + wr++; + mtfFreq[BZip2Constants.RUNA]++; + break; + case 1: + szptr[wr] = (short) BZip2Constants.RUNB; + wr++; + mtfFreq[BZip2Constants.RUNB]++; + break; + } + if (zPend < 2) { + break; + } + zPend = (zPend - 2) / 2; + } + } + + szptr[wr] = (short) EOB; + wr++; + mtfFreq[EOB]++; + + nMTF = wr; + } + + public override int Read(byte[] buffer, int offset, int count) { + return 0; + } + + public override long Seek(long offset, SeekOrigin origin) { + return 0; + } + + public override void SetLength(long value) { + } + + public override void Write(byte[] buffer, int offset, int count) { + for (int k = 0; k < count; ++k) { + WriteByte(buffer[k + offset]); + } + } + + public override bool CanRead { + get { + return false; + } + } + + public override bool CanSeek { + get { + return false; + } + } + + public override bool CanWrite { + get { + return true; + } + } + + public override long Length { + get { + return 0; + } + } + + public override long Position { + get { + return 0; + } + set { + } + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/bzip2/CRC.cs b/src/core/srcbc/util/bzip2/CRC.cs new file mode 100644 index 0000000..c1eb57c --- /dev/null +++ b/src/core/srcbc/util/bzip2/CRC.cs @@ -0,0 +1,170 @@ +using System; + +/* + * The Apache Software License), Version 1.1 + * + * Copyright (c) 2001-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms), with or without + * modification), are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice), this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution), if + * any), must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately), this acknowlegement may appear in the software itself), + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Ant" and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission), please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES), INCLUDING), BUT NOT LIMITED TO), THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT), INDIRECT), INCIDENTAL), + * SPECIAL), EXEMPLARY), OR CONSEQUENTIAL DAMAGES (INCLUDING), BUT NOT + * LIMITED TO), PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE), DATA), OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY), WHETHER IN CONTRACT), STRICT LIABILITY), + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE), EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation), please see + * . + */ + +/* + * This package is based on the work done by Keiron Liddle), Aftex Software + * to whom the Ant project is very grateful for his + * great code. + */ + +namespace Org.BouncyCastle.Apache.Bzip2 +{ + /** + * A simple class the hold and calculate the CRC for sanity checking + * of the data. + * + * @author Keiron Liddle + */ + internal class CRC + { + public static int[] crc32Table = { + unchecked((int)0x00000000), unchecked((int)0x04c11db7), unchecked((int)0x09823b6e), unchecked((int)0x0d4326d9), + unchecked((int)0x130476dc), unchecked((int)0x17c56b6b), unchecked((int)0x1a864db2), unchecked((int)0x1e475005), + unchecked((int)0x2608edb8), unchecked((int)0x22c9f00f), unchecked((int)0x2f8ad6d6), unchecked((int)0x2b4bcb61), + unchecked((int)0x350c9b64), unchecked((int)0x31cd86d3), unchecked((int)0x3c8ea00a), unchecked((int)0x384fbdbd), + unchecked((int)0x4c11db70), unchecked((int)0x48d0c6c7), unchecked((int)0x4593e01e), unchecked((int)0x4152fda9), + unchecked((int)0x5f15adac), unchecked((int)0x5bd4b01b), unchecked((int)0x569796c2), unchecked((int)0x52568b75), + unchecked((int)0x6a1936c8), unchecked((int)0x6ed82b7f), unchecked((int)0x639b0da6), unchecked((int)0x675a1011), + unchecked((int)0x791d4014), unchecked((int)0x7ddc5da3), unchecked((int)0x709f7b7a), unchecked((int)0x745e66cd), + unchecked((int)0x9823b6e0), unchecked((int)0x9ce2ab57), unchecked((int)0x91a18d8e), unchecked((int)0x95609039), + unchecked((int)0x8b27c03c), unchecked((int)0x8fe6dd8b), unchecked((int)0x82a5fb52), unchecked((int)0x8664e6e5), + unchecked((int)0xbe2b5b58), unchecked((int)0xbaea46ef), unchecked((int)0xb7a96036), unchecked((int)0xb3687d81), + unchecked((int)0xad2f2d84), unchecked((int)0xa9ee3033), unchecked((int)0xa4ad16ea), unchecked((int)0xa06c0b5d), + unchecked((int)0xd4326d90), unchecked((int)0xd0f37027), unchecked((int)0xddb056fe), unchecked((int)0xd9714b49), + unchecked((int)0xc7361b4c), unchecked((int)0xc3f706fb), unchecked((int)0xceb42022), unchecked((int)0xca753d95), + unchecked((int)0xf23a8028), unchecked((int)0xf6fb9d9f), unchecked((int)0xfbb8bb46), unchecked((int)0xff79a6f1), + unchecked((int)0xe13ef6f4), unchecked((int)0xe5ffeb43), unchecked((int)0xe8bccd9a), unchecked((int)0xec7dd02d), + unchecked((int)0x34867077), unchecked((int)0x30476dc0), unchecked((int)0x3d044b19), unchecked((int)0x39c556ae), + unchecked((int)0x278206ab), unchecked((int)0x23431b1c), unchecked((int)0x2e003dc5), unchecked((int)0x2ac12072), + unchecked((int)0x128e9dcf), unchecked((int)0x164f8078), unchecked((int)0x1b0ca6a1), unchecked((int)0x1fcdbb16), + unchecked((int)0x018aeb13), unchecked((int)0x054bf6a4), unchecked((int)0x0808d07d), unchecked((int)0x0cc9cdca), + unchecked((int)0x7897ab07), unchecked((int)0x7c56b6b0), unchecked((int)0x71159069), unchecked((int)0x75d48dde), + unchecked((int)0x6b93dddb), unchecked((int)0x6f52c06c), unchecked((int)0x6211e6b5), unchecked((int)0x66d0fb02), + unchecked((int)0x5e9f46bf), unchecked((int)0x5a5e5b08), unchecked((int)0x571d7dd1), unchecked((int)0x53dc6066), + unchecked((int)0x4d9b3063), unchecked((int)0x495a2dd4), unchecked((int)0x44190b0d), unchecked((int)0x40d816ba), + unchecked((int)0xaca5c697), unchecked((int)0xa864db20), unchecked((int)0xa527fdf9), unchecked((int)0xa1e6e04e), + unchecked((int)0xbfa1b04b), unchecked((int)0xbb60adfc), unchecked((int)0xb6238b25), unchecked((int)0xb2e29692), + unchecked((int)0x8aad2b2f), unchecked((int)0x8e6c3698), unchecked((int)0x832f1041), unchecked((int)0x87ee0df6), + unchecked((int)0x99a95df3), unchecked((int)0x9d684044), unchecked((int)0x902b669d), unchecked((int)0x94ea7b2a), + unchecked((int)0xe0b41de7), unchecked((int)0xe4750050), unchecked((int)0xe9362689), unchecked((int)0xedf73b3e), + unchecked((int)0xf3b06b3b), unchecked((int)0xf771768c), unchecked((int)0xfa325055), unchecked((int)0xfef34de2), + unchecked((int)0xc6bcf05f), unchecked((int)0xc27dede8), unchecked((int)0xcf3ecb31), unchecked((int)0xcbffd686), + unchecked((int)0xd5b88683), unchecked((int)0xd1799b34), unchecked((int)0xdc3abded), unchecked((int)0xd8fba05a), + unchecked((int)0x690ce0ee), unchecked((int)0x6dcdfd59), unchecked((int)0x608edb80), unchecked((int)0x644fc637), + unchecked((int)0x7a089632), unchecked((int)0x7ec98b85), unchecked((int)0x738aad5c), unchecked((int)0x774bb0eb), + unchecked((int)0x4f040d56), unchecked((int)0x4bc510e1), unchecked((int)0x46863638), unchecked((int)0x42472b8f), + unchecked((int)0x5c007b8a), unchecked((int)0x58c1663d), unchecked((int)0x558240e4), unchecked((int)0x51435d53), + unchecked((int)0x251d3b9e), unchecked((int)0x21dc2629), unchecked((int)0x2c9f00f0), unchecked((int)0x285e1d47), + unchecked((int)0x36194d42), unchecked((int)0x32d850f5), unchecked((int)0x3f9b762c), unchecked((int)0x3b5a6b9b), + unchecked((int)0x0315d626), unchecked((int)0x07d4cb91), unchecked((int)0x0a97ed48), unchecked((int)0x0e56f0ff), + unchecked((int)0x1011a0fa), unchecked((int)0x14d0bd4d), unchecked((int)0x19939b94), unchecked((int)0x1d528623), + unchecked((int)0xf12f560e), unchecked((int)0xf5ee4bb9), unchecked((int)0xf8ad6d60), unchecked((int)0xfc6c70d7), + unchecked((int)0xe22b20d2), unchecked((int)0xe6ea3d65), unchecked((int)0xeba91bbc), unchecked((int)0xef68060b), + unchecked((int)0xd727bbb6), unchecked((int)0xd3e6a601), unchecked((int)0xdea580d8), unchecked((int)0xda649d6f), + unchecked((int)0xc423cd6a), unchecked((int)0xc0e2d0dd), unchecked((int)0xcda1f604), unchecked((int)0xc960ebb3), + unchecked((int)0xbd3e8d7e), unchecked((int)0xb9ff90c9), unchecked((int)0xb4bcb610), unchecked((int)0xb07daba7), + unchecked((int)0xae3afba2), unchecked((int)0xaafbe615), unchecked((int)0xa7b8c0cc), unchecked((int)0xa379dd7b), + unchecked((int)0x9b3660c6), unchecked((int)0x9ff77d71), unchecked((int)0x92b45ba8), unchecked((int)0x9675461f), + unchecked((int)0x8832161a), unchecked((int)0x8cf30bad), unchecked((int)0x81b02d74), unchecked((int)0x857130c3), + unchecked((int)0x5d8a9099), unchecked((int)0x594b8d2e), unchecked((int)0x5408abf7), unchecked((int)0x50c9b640), + unchecked((int)0x4e8ee645), unchecked((int)0x4a4ffbf2), unchecked((int)0x470cdd2b), unchecked((int)0x43cdc09c), + unchecked((int)0x7b827d21), unchecked((int)0x7f436096), unchecked((int)0x7200464f), unchecked((int)0x76c15bf8), + unchecked((int)0x68860bfd), unchecked((int)0x6c47164a), unchecked((int)0x61043093), unchecked((int)0x65c52d24), + unchecked((int)0x119b4be9), unchecked((int)0x155a565e), unchecked((int)0x18197087), unchecked((int)0x1cd86d30), + unchecked((int)0x029f3d35), unchecked((int)0x065e2082), unchecked((int)0x0b1d065b), unchecked((int)0x0fdc1bec), + unchecked((int)0x3793a651), unchecked((int)0x3352bbe6), unchecked((int)0x3e119d3f), unchecked((int)0x3ad08088), + unchecked((int)0x2497d08d), unchecked((int)0x2056cd3a), unchecked((int)0x2d15ebe3), unchecked((int)0x29d4f654), + unchecked((int)0xc5a92679), unchecked((int)0xc1683bce), unchecked((int)0xcc2b1d17), unchecked((int)0xc8ea00a0), + unchecked((int)0xd6ad50a5), unchecked((int)0xd26c4d12), unchecked((int)0xdf2f6bcb), unchecked((int)0xdbee767c), + unchecked((int)0xe3a1cbc1), unchecked((int)0xe760d676), unchecked((int)0xea23f0af), unchecked((int)0xeee2ed18), + unchecked((int)0xf0a5bd1d), unchecked((int)0xf464a0aa), unchecked((int)0xf9278673), unchecked((int)0xfde69bc4), + unchecked((int)0x89b8fd09), unchecked((int)0x8d79e0be), unchecked((int)0x803ac667), unchecked((int)0x84fbdbd0), + unchecked((int)0x9abc8bd5), unchecked((int)0x9e7d9662), unchecked((int)0x933eb0bb), unchecked((int)0x97ffad0c), + unchecked((int)0xafb010b1), unchecked((int)0xab710d06), unchecked((int)0xa6322bdf), unchecked((int)0xa2f33668), + unchecked((int)0xbcb4666d), unchecked((int)0xb8757bda), unchecked((int)0xb5365d03), unchecked((int)0xb1f740b4) + }; + + public CRC() { + InitialiseCRC(); + } + + internal void InitialiseCRC() { + globalCrc = unchecked((int)0xffffffff); + } + + internal int GetFinalCRC() { + return ~globalCrc; + } + + internal int GetGlobalCRC() { + return globalCrc; + } + + internal void SetGlobalCRC(int newCrc) { + globalCrc = newCrc; + } + + internal void UpdateCRC(int inCh) { + int temp = (globalCrc >> 24) ^ inCh; + if (temp < 0) { + temp = 256 + temp; + } + globalCrc = (globalCrc << 8) ^ CRC.crc32Table[temp]; + } + + internal int globalCrc; + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/collections/CollectionUtilities.cs b/src/core/srcbc/util/collections/CollectionUtilities.cs new file mode 100644 index 0000000..c845c83 --- /dev/null +++ b/src/core/srcbc/util/collections/CollectionUtilities.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using System.Text; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public sealed class CollectionUtilities + { + private CollectionUtilities() + { + } + + public static bool CheckElementsAreOfType( + IEnumerable e, + Type t) + { + foreach (object o in e) + { + if (!t.IsInstanceOfType(o)) + return false; + } + return true; + } + + public static string ToString( + IEnumerable c) + { + StringBuilder sb = new StringBuilder("["); + + IEnumerator e = c.GetEnumerator(); + + if (e.MoveNext()) + { + sb.Append(e.Current.ToString()); + + while (e.MoveNext()) + { + sb.Append(", "); + sb.Append(e.Current.ToString()); + } + } + + sb.Append(']'); + + return sb.ToString(); + } + } +} diff --git a/src/core/srcbc/util/collections/EmptyEnumerable.cs b/src/core/srcbc/util/collections/EmptyEnumerable.cs new file mode 100644 index 0000000..a34b0af --- /dev/null +++ b/src/core/srcbc/util/collections/EmptyEnumerable.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public sealed class EmptyEnumerable + : IEnumerable + { + public static readonly IEnumerable Instance = new EmptyEnumerable(); + + private EmptyEnumerable() + { + } + + public IEnumerator GetEnumerator() + { + return EmptyEnumerator.Instance; + } + } + + public sealed class EmptyEnumerator + : IEnumerator + { + public static readonly IEnumerator Instance = new EmptyEnumerator(); + + private EmptyEnumerator() + { + } + + public bool MoveNext() + { + return false; + } + + public void Reset() + { + } + + public object Current + { + get { throw new InvalidOperationException("No elements"); } + } + } +} diff --git a/src/core/srcbc/util/collections/EnumerableProxy.cs b/src/core/srcbc/util/collections/EnumerableProxy.cs new file mode 100644 index 0000000..6ac9893 --- /dev/null +++ b/src/core/srcbc/util/collections/EnumerableProxy.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public sealed class EnumerableProxy + : IEnumerable + { + private readonly IEnumerable inner; + + public EnumerableProxy( + IEnumerable inner) + { + if (inner == null) + throw new ArgumentNullException("inner"); + + this.inner = inner; + } + + public IEnumerator GetEnumerator() + { + return inner.GetEnumerator(); + } + } +} diff --git a/src/core/srcbc/util/collections/HashSet.cs b/src/core/srcbc/util/collections/HashSet.cs new file mode 100644 index 0000000..fef6e83 --- /dev/null +++ b/src/core/srcbc/util/collections/HashSet.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public class HashSet + : ISet + { + private readonly Hashtable impl = new Hashtable(); + + public HashSet() + { + } + + public HashSet(ISet s) + { + foreach (object o in s) + { + Add(o); + } + } + + public void Add(object o) + { + impl[o] = null; + } + + public bool Contains(object o) + { + return impl.ContainsKey(o); + } + + public void CopyTo(Array array, int index) + { + impl.Keys.CopyTo(array, index); + } + + public int Count + { + get { return impl.Count; } + } + + public IEnumerator GetEnumerator() + { + return impl.Keys.GetEnumerator(); + } + + public bool IsSynchronized + { + get { return impl.IsSynchronized; } + } + + public void Remove(object o) + { + impl.Remove(o); + } + + public object SyncRoot + { + get { return impl.SyncRoot; } + } + } +} diff --git a/src/core/srcbc/util/collections/ISet.cs b/src/core/srcbc/util/collections/ISet.cs new file mode 100644 index 0000000..3e0a1b2 --- /dev/null +++ b/src/core/srcbc/util/collections/ISet.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public interface ISet + : ICollection + { + void Add(object o); + bool Contains(object o); + void Remove(object o); + } +} diff --git a/src/core/srcbc/util/date/DateTimeObject.cs b/src/core/srcbc/util/date/DateTimeObject.cs new file mode 100644 index 0000000..f5be6ce --- /dev/null +++ b/src/core/srcbc/util/date/DateTimeObject.cs @@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Date +{ + public sealed class DateTimeObject + { + private readonly DateTime dt; + + public DateTimeObject( + DateTime dt) + { + this.dt = dt; + } + + public DateTime Value + { + get { return dt; } + } + + public override string ToString() + { + return dt.ToString(); + } + } +} diff --git a/src/core/srcbc/util/date/DateTimeUtilities.cs b/src/core/srcbc/util/date/DateTimeUtilities.cs new file mode 100644 index 0000000..449934d --- /dev/null +++ b/src/core/srcbc/util/date/DateTimeUtilities.cs @@ -0,0 +1,47 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Date +{ + public class DateTimeUtilities + { + public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1); + + private DateTimeUtilities() + { + } + + /// + /// Return the number of milliseconds since the Unix epoch (1 Jan., 1970 UTC) for a given DateTime value. + /// + /// A UTC DateTime value not before epoch. + /// Number of whole milliseconds after epoch. + /// 'dateTime' is before epoch. + public static long DateTimeToUnixMs( + DateTime dateTime) + { + if (dateTime.CompareTo(UnixEpoch) < 0) + throw new ArgumentException("DateTime value may not be before the epoch", "dateTime"); + + return (dateTime.Ticks - UnixEpoch.Ticks) / TimeSpan.TicksPerMillisecond; + } + + /// + /// Create a DateTime value from the number of milliseconds since the Unix epoch (1 Jan., 1970 UTC). + /// + /// Number of milliseconds since the epoch. + /// A UTC DateTime value + public static DateTime UnixMsToDateTime( + long unixMs) + { + return new DateTime(unixMs * TimeSpan.TicksPerMillisecond + UnixEpoch.Ticks); + } + + /// + /// Return the current number of milliseconds since the Unix epoch (1 Jan., 1970 UTC). + /// + public static long CurrentUnixMs() + { + return DateTimeToUnixMs(DateTime.UtcNow); + } + } +} diff --git a/src/core/srcbc/util/encoders/Base64.cs b/src/core/srcbc/util/encoders/Base64.cs new file mode 100644 index 0000000..66c1e07 --- /dev/null +++ b/src/core/srcbc/util/encoders/Base64.cs @@ -0,0 +1,115 @@ +using System; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + public sealed class Base64 + { +// private static readonly IEncoder encoder = new Base64Encoder(); + + private Base64() + { + } + + /** + * encode the input data producing a base 64 encoded byte array. + * + * @return a byte array containing the base 64 encoded data. + */ + public static byte[] Encode( + byte[] data) + { + string s = Convert.ToBase64String(data, 0, data.Length); + return Encoding.ASCII.GetBytes(s); + +// MemoryStream bOut = new MemoryStream(); +// encoder.Encode(data, 0, data.Length, bOut); +// return bOut.ToArray(); + } + + /** + * Encode the byte data to base 64 writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int Encode( + byte[] data, + Stream outStream) + { + string s = Convert.ToBase64String(data, 0, data.Length); + byte[] encoded = Encoding.ASCII.GetBytes(s); + outStream.Write(encoded, 0, encoded.Length); + return encoded.Length; + +// return encoder.Encode(data, 0, data.Length, outStream); + } + + /** + * Encode the byte data to base 64 writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int Encode( + byte[] data, + int off, + int length, + Stream outStream) + { + string s = Convert.ToBase64String(data, off, length); + byte[] encoded = Encoding.ASCII.GetBytes(s); + outStream.Write(encoded, 0, encoded.Length); + return encoded.Length; + +// return encoder.Encode(data, off, length, outStream); + } + + /** + * decode the base 64 encoded input data. It is assumed the input data is valid. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + byte[] data) + { + string s = Encoding.ASCII.GetString(data, 0, data.Length); + return Convert.FromBase64String(s); + +// MemoryStream bOut = new MemoryStream(); +// encoder.Decode(data, 0, data.Length, bOut); +// return bOut.ToArray(); + } + + /** + * decode the base 64 encoded string data - whitespace will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + string data) + { + return Convert.FromBase64String(data); + +// MemoryStream bOut = new MemoryStream(); +// encoder.DecodeString(data, bOut); +// return bOut.ToArray(); + } + + /** + * decode the base 64 encoded string data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int Decode( + string data, + Stream outStream) + { + byte[] decoded = Decode(data); + outStream.Write(decoded, 0, decoded.Length); + return decoded.Length; + +// return encoder.DecodeString(data, outStream); + } + } +} diff --git a/src/core/srcbc/util/encoders/Base64Encoder.cs b/src/core/srcbc/util/encoders/Base64Encoder.cs new file mode 100644 index 0000000..45fc1bb --- /dev/null +++ b/src/core/srcbc/util/encoders/Base64Encoder.cs @@ -0,0 +1,307 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + public class Base64Encoder + : IEncoder + { + protected readonly byte[] encodingTable = + { + (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', + (byte)'+', (byte)'/' + }; + + protected byte padding = (byte)'='; + + /* + * set up the decoding table. + */ + protected readonly byte[] decodingTable = new byte[128]; + + protected void InitialiseDecodingTable() + { + for (int i = 0; i < encodingTable.Length; i++) + { + decodingTable[encodingTable[i]] = (byte)i; + } + } + + public Base64Encoder() + { + InitialiseDecodingTable(); + } + + /** + * encode the input data producing a base 64 output stream. + * + * @return the number of bytes produced. + */ + public int Encode( + byte[] data, + int off, + int length, + Stream outStream) + { + int modulus = length % 3; + int dataLength = (length - modulus); + int a1, a2, a3; + + for (int i = off; i < off + dataLength; i += 3) + { + a1 = data[i] & 0xff; + a2 = data[i + 1] & 0xff; + a3 = data[i + 2] & 0xff; + + outStream.WriteByte(encodingTable[(int) ((uint) a1 >> 2) & 0x3f]); + outStream.WriteByte(encodingTable[((a1 << 4) | (int) ((uint) a2 >> 4)) & 0x3f]); + outStream.WriteByte(encodingTable[((a2 << 2) | (int) ((uint) a3 >> 6)) & 0x3f]); + outStream.WriteByte(encodingTable[a3 & 0x3f]); + } + + /* + * process the tail end. + */ + int b1, b2, b3; + int d1, d2; + + switch (modulus) + { + case 0: /* nothing left to do */ + break; + case 1: + d1 = data[off + dataLength] & 0xff; + b1 = (d1 >> 2) & 0x3f; + b2 = (d1 << 4) & 0x3f; + + outStream.WriteByte(encodingTable[b1]); + outStream.WriteByte(encodingTable[b2]); + outStream.WriteByte(padding); + outStream.WriteByte(padding); + break; + case 2: + d1 = data[off + dataLength] & 0xff; + d2 = data[off + dataLength + 1] & 0xff; + + b1 = (d1 >> 2) & 0x3f; + b2 = ((d1 << 4) | (d2 >> 4)) & 0x3f; + b3 = (d2 << 2) & 0x3f; + + outStream.WriteByte(encodingTable[b1]); + outStream.WriteByte(encodingTable[b2]); + outStream.WriteByte(encodingTable[b3]); + outStream.WriteByte(padding); + break; + } + + return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4); + } + + private bool ignore( + char c) + { + return (c == '\n' || c =='\r' || c == '\t' || c == ' '); + } + + /** + * decode the base 64 encoded byte data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public int Decode( + byte[] data, + int off, + int length, + Stream outStream) + { + byte b1, b2, b3, b4; + int outLen = 0; + + int end = off + length; + + while (end > off) + { + if (!ignore((char)data[end - 1])) + { + break; + } + + end--; + } + + int i = off; + int finish = end - 4; + + i = nextI(data, i, finish); + + while (i < finish) + { + b1 = decodingTable[data[i++]]; + + i = nextI(data, i, finish); + + b2 = decodingTable[data[i++]]; + + i = nextI(data, i, finish); + + b3 = decodingTable[data[i++]]; + + i = nextI(data, i, finish); + + b4 = decodingTable[data[i++]]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + outStream.WriteByte((byte)((b3 << 6) | b4)); + + outLen += 3; + + i = nextI(data, i, finish); + } + + outLen += decodeLastBlock(outStream, (char)data[end - 4], (char)data[end - 3], (char)data[end - 2], (char)data[end - 1]); + + return outLen; + } + + private int nextI( + byte[] data, + int i, + int finish) + { + while ((i < finish) && ignore((char)data[i])) + { + i++; + } + return i; + } + + /** + * decode the base 64 encoded string data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public int DecodeString( + string data, + Stream outStream) + { + // Platform Implementation +// byte[] bytes = Convert.FromBase64String(data); +// outStream.Write(bytes, 0, bytes.Length); +// return bytes.Length; + + byte b1, b2, b3, b4; + int length = 0; + + int end = data.Length; + + while (end > 0) + { + if (!ignore(data[end - 1])) + { + break; + } + + end--; + } + + int i = 0; + int finish = end - 4; + + i = nextI(data, i, finish); + + while (i < finish) + { + b1 = decodingTable[data[i++]]; + + i = nextI(data, i, finish); + + b2 = decodingTable[data[i++]]; + + i = nextI(data, i, finish); + + b3 = decodingTable[data[i++]]; + + i = nextI(data, i, finish); + + b4 = decodingTable[data[i++]]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + outStream.WriteByte((byte)((b3 << 6) | b4)); + + length += 3; + + i = nextI(data, i, finish); + } + + length += decodeLastBlock(outStream, data[end - 4], data[end - 3], data[end - 2], data[end - 1]); + + return length; + } + + private int decodeLastBlock( + Stream outStream, + char c1, + char c2, + char c3, + char c4) + { + if (c3 == padding) + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + + return 1; + } + + if (c4 == padding) + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; + byte b3 = decodingTable[c3]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + + return 2; + } + + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; + byte b3 = decodingTable[c3]; + byte b4 = decodingTable[c4]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + outStream.WriteByte((byte)((b3 << 6) | b4)); + + return 3; + } + } + + private int nextI(string data, int i, int finish) + { + while ((i < finish) && ignore(data[i])) + { + i++; + } + return i; + } + } +} diff --git a/src/core/srcbc/util/encoders/BufferedDecoder.cs b/src/core/srcbc/util/encoders/BufferedDecoder.cs new file mode 100644 index 0000000..e43d96d --- /dev/null +++ b/src/core/srcbc/util/encoders/BufferedDecoder.cs @@ -0,0 +1,117 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// + /// A buffering class to allow translation from one format to another to + /// be done in discrete chunks. + /// + public class BufferedDecoder + { + internal byte[] buffer; + internal int bufOff; + + internal ITranslator translator; + + /// + /// Create a buffered Decoder. + /// + /// The translater to use. + /// The size of the buffer. + public BufferedDecoder( + ITranslator translator, + int bufferSize) + { + this.translator = translator; + + if ((bufferSize % translator.GetEncodedBlockSize()) != 0) + { + throw new ArgumentException("buffer size not multiple of input block size"); + } + + buffer = new byte[bufferSize]; +// bufOff = 0; + } + + /// + /// Process one byte of data. + /// + /// Data in. + /// Byte array for the output. + /// The offset in the output byte array to start writing from. + /// The amount of output bytes. + public int ProcessByte( + byte input, + byte[] output, + int outOff) + { + int resultLen = 0; + + buffer[bufOff++] = input; + + if (bufOff == buffer.Length) + { + resultLen = translator.Decode(buffer, 0, buffer.Length, output, outOff); + bufOff = 0; + } + + return resultLen; + } + + + /// + /// Process data from a byte array. + /// + /// The input data. + /// Start position within input data array. + /// Amount of data to process from input data array. + /// Array to store output. + /// Position in output array to start writing from. + /// The amount of output bytes. + public int ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] outBytes, + int outOff) + { + if (len < 0) + { + throw new ArgumentException("Can't have a negative input length!"); + } + + int resultLen = 0; + int gapLen = buffer.Length - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, buffer, bufOff, gapLen); + + resultLen += translator.Decode(buffer, 0, buffer.Length, outBytes, outOff); + + bufOff = 0; + + len -= gapLen; + inOff += gapLen; + outOff += resultLen; + + int chunkSize = len - (len % buffer.Length); + + resultLen += translator.Decode(input, inOff, chunkSize, outBytes, outOff); + + len -= chunkSize; + inOff += chunkSize; + } + + if (len != 0) + { + Array.Copy(input, inOff, buffer, bufOff, len); + + bufOff += len; + } + + return resultLen; + } + } + +} diff --git a/src/core/srcbc/util/encoders/BufferedEncoder.cs b/src/core/srcbc/util/encoders/BufferedEncoder.cs new file mode 100644 index 0000000..20c236f --- /dev/null +++ b/src/core/srcbc/util/encoders/BufferedEncoder.cs @@ -0,0 +1,117 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// + /// A class that allows encoding of data using a specific encoder to be processed in chunks. + /// + public class BufferedEncoder + { + internal byte[] Buffer; + internal int bufOff; + + internal ITranslator translator; + + + /// + /// Create. + /// + /// The translator to use. + /// Size of the chunks. + public BufferedEncoder( + ITranslator translator, + int bufferSize) + { + this.translator = translator; + + if ((bufferSize % translator.GetEncodedBlockSize()) != 0) + { + throw new ArgumentException("buffer size not multiple of input block size"); + } + + Buffer = new byte[bufferSize]; +// bufOff = 0; + } + + + /// + /// Process one byte of data. + /// + /// The byte. + /// An array to store output in. + /// Offset within output array to start writing from. + /// + public int ProcessByte( + byte input, + byte[] outBytes, + int outOff) + { + int resultLen = 0; + + Buffer[bufOff++] = input; + + if (bufOff == Buffer.Length) + { + resultLen = translator.Encode(Buffer, 0, Buffer.Length, outBytes, outOff); + bufOff = 0; + } + + return resultLen; + } + + /// + /// Process data from a byte array. + /// + /// Input data Byte array containing data to be processed. + /// Start position within input data array. + /// Amount of input data to be processed. + /// Output data array. + /// Offset within output data array to start writing to. + /// The amount of data written. + public int ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] outBytes, + int outOff) + { + if (len < 0) + { + throw new ArgumentException("Can't have a negative input length!"); + } + + int resultLen = 0; + int gapLen = Buffer.Length - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, Buffer, bufOff, gapLen); + + resultLen += translator.Encode(Buffer, 0, Buffer.Length, outBytes, outOff); + + bufOff = 0; + + len -= gapLen; + inOff += gapLen; + outOff += resultLen; + + int chunkSize = len - (len % Buffer.Length); + + resultLen += translator.Encode(input, inOff, chunkSize, outBytes, outOff); + + len -= chunkSize; + inOff += chunkSize; + } + + if (len != 0) + { + Array.Copy(input, inOff, Buffer, bufOff, len); + + bufOff += len; + } + + return resultLen; + } + } + +} diff --git a/src/core/srcbc/util/encoders/Hex.cs b/src/core/srcbc/util/encoders/Hex.cs new file mode 100644 index 0000000..fe63b18 --- /dev/null +++ b/src/core/srcbc/util/encoders/Hex.cs @@ -0,0 +1,114 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// + /// Class to decode and encode Hex. + /// + public sealed class Hex + { + private static readonly IEncoder encoder = new HexEncoder(); + + private Hex() + { + } + + /** + * encode the input data producing a Hex encoded byte array. + * + * @return a byte array containing the Hex encoded data. + */ + public static byte[] Encode( + byte[] data) + { + return Encode(data, 0, data.Length); + } + + /** + * encode the input data producing a Hex encoded byte array. + * + * @return a byte array containing the Hex encoded data. + */ + public static byte[] Encode( + byte[] data, + int off, + int length) + { + MemoryStream bOut = new MemoryStream(length * 2); + + encoder.Encode(data, off, length, bOut); + + return bOut.ToArray(); + } + + /** + * Hex encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int Encode( + byte[] data, + Stream outStream) + { + return encoder.Encode(data, 0, data.Length, outStream); + } + + /** + * Hex encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int Encode( + byte[] data, + int off, + int length, + Stream outStream) + { + return encoder.Encode(data, off, length, outStream); + } + + /** + * decode the Hex encoded input data. It is assumed the input data is valid. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + byte[] data) + { + MemoryStream bOut = new MemoryStream((data.Length + 1) / 2); + + encoder.Decode(data, 0, data.Length, bOut); + + return bOut.ToArray(); + } + + /** + * decode the Hex encoded string data - whitespace will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + string data) + { + MemoryStream bOut = new MemoryStream((data.Length + 1) / 2); + + encoder.DecodeString(data, bOut); + + return bOut.ToArray(); + } + + /** + * decode the Hex encoded string data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int Decode( + string data, + Stream outStream) + { + return encoder.DecodeString(data, outStream); + } + } +} diff --git a/src/core/srcbc/util/encoders/HexEncoder.cs b/src/core/srcbc/util/encoders/HexEncoder.cs new file mode 100644 index 0000000..b850570 --- /dev/null +++ b/src/core/srcbc/util/encoders/HexEncoder.cs @@ -0,0 +1,164 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + public class HexEncoder + : IEncoder + { + private static readonly byte[] encodingTable = + { + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', + (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f' + }; + + /* + * set up the decoding table. + */ + internal static readonly byte[] decodingTable = new byte[128]; + + static HexEncoder() + { + for (int i = 0; i < encodingTable.Length; i++) + { + decodingTable[encodingTable[i]] = (byte)i; + } + + decodingTable['A'] = decodingTable['a']; + decodingTable['B'] = decodingTable['b']; + decodingTable['C'] = decodingTable['c']; + decodingTable['D'] = decodingTable['d']; + decodingTable['E'] = decodingTable['e']; + decodingTable['F'] = decodingTable['f']; + } + + /** + * encode the input data producing a Hex output stream. + * + * @return the number of bytes produced. + */ + public int Encode( + byte[] data, + int off, + int length, + Stream outStream) + { + for (int i = off; i < (off + length); i++) + { + int v = data[i]; + + outStream.WriteByte(encodingTable[v >> 4]); + outStream.WriteByte(encodingTable[v & 0xf]); + } + + return length * 2; + } + + private bool ignore( + char c) + { + return (c == '\n' || c =='\r' || c == '\t' || c == ' '); + } + + /** + * decode the Hex encoded byte data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public int Decode( + byte[] data, + int off, + int length, + Stream outStream) + { + byte b1, b2; + int outLen = 0; + int end = off + length; + + while (end > off) + { + if (!ignore((char)data[end - 1])) + { + break; + } + + end--; + } + + int i = off; + while (i < end) + { + while (i < end && ignore((char)data[i])) + { + i++; + } + + b1 = decodingTable[data[i++]]; + + while (i < end && ignore((char)data[i])) + { + i++; + } + + b2 = decodingTable[data[i++]]; + + outStream.WriteByte((byte)((b1 << 4) | b2)); + + outLen++; + } + + return outLen; + } + + /** + * decode the Hex encoded string data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public int DecodeString( + string data, + Stream outStream) + { + byte b1, b2; + int length = 0; + + int end = data.Length; + + while (end > 0) + { + if (!ignore(data[end - 1])) + { + break; + } + + end--; + } + + int i = 0; + while (i < end) + { + while (i < end && ignore(data[i])) + { + i++; + } + + b1 = decodingTable[data[i++]]; + + while (i < end && ignore(data[i])) + { + i++; + } + + b2 = decodingTable[data[i++]]; + + outStream.WriteByte((byte)((b1 << 4) | b2)); + + length++; + } + + return length; + } + } +} diff --git a/src/core/srcbc/util/encoders/HexTranslator.cs b/src/core/srcbc/util/encoders/HexTranslator.cs new file mode 100644 index 0000000..3db3b55 --- /dev/null +++ b/src/core/srcbc/util/encoders/HexTranslator.cs @@ -0,0 +1,108 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// + /// A hex translator. + /// + public class HexTranslator : ITranslator + { + private static readonly byte[] hexTable = + { + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', + (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f' + }; + + /// + /// Return encoded block size. + /// + /// 2 + public int GetEncodedBlockSize() + { + return 2; + } + + /// + /// Encode some data. + /// + /// Input data array. + /// Start position within input data array. + /// The amount of data to process. + /// The output data array. + /// The offset within the output data array to start writing from. + /// Amount of data encoded. + public int Encode( + byte[] input, + int inOff, + int length, + byte[] outBytes, + int outOff) + { + for (int i = 0, j = 0; i < length; i++, j += 2) + { + outBytes[outOff + j] = hexTable[(input[inOff] >> 4) & 0x0f]; + outBytes[outOff + j + 1] = hexTable[input[inOff] & 0x0f]; + + inOff++; + } + + return length * 2; + } + + /// + /// Returns the decoded block size. + /// + /// 1 + public int GetDecodedBlockSize() + { + return 1; + } + + /// + /// Decode data from a byte array. + /// + /// The input data array. + /// Start position within input data array. + /// The amounty of data to process. + /// The output data array. + /// The position within the output data array to start writing from. + /// The amount of data written. + public int Decode( + byte[] input, + int inOff, + int length, + byte[] outBytes, + int outOff) + { + int halfLength = length / 2; + byte left, right; + for (int i = 0; i < halfLength; i++) + { + left = input[inOff + i * 2]; + right = input[inOff + i * 2 + 1]; + + if (left < (byte)'a') + { + outBytes[outOff] = (byte)((left - '0') << 4); + } + else + { + outBytes[outOff] = (byte)((left - 'a' + 10) << 4); + } + if (right < (byte)'a') + { + outBytes[outOff] += (byte)(right - '0'); + } + else + { + outBytes[outOff] += (byte)(right - 'a' + 10); + } + + outOff++; + } + + return halfLength; + } + } + +} diff --git a/src/core/srcbc/util/encoders/IEncoder.cs b/src/core/srcbc/util/encoders/IEncoder.cs new file mode 100644 index 0000000..4a1aca3 --- /dev/null +++ b/src/core/srcbc/util/encoders/IEncoder.cs @@ -0,0 +1,18 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /** + * Encode and decode byte arrays (typically from binary to 7-bit ASCII + * encodings). + */ + public interface IEncoder + { + int Encode(byte[] data, int off, int length, Stream outStream); + + int Decode(byte[] data, int off, int length, Stream outStream); + + int DecodeString(string data, Stream outStream); + } +} diff --git a/src/core/srcbc/util/encoders/Translator.cs b/src/core/srcbc/util/encoders/Translator.cs new file mode 100644 index 0000000..3231432 --- /dev/null +++ b/src/core/srcbc/util/encoders/Translator.cs @@ -0,0 +1,19 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// + /// Translator interface. + /// + public interface ITranslator + { + int GetEncodedBlockSize(); + + int Encode(byte[] input, int inOff, int length, byte[] outBytes, int outOff); + + int GetDecodedBlockSize(); + + int Decode(byte[] input, int inOff, int length, byte[] outBytes, int outOff); + } + +} diff --git a/src/core/srcbc/util/encoders/UrlBase64.cs b/src/core/srcbc/util/encoders/UrlBase64.cs new file mode 100644 index 0000000..f88d82d --- /dev/null +++ b/src/core/srcbc/util/encoders/UrlBase64.cs @@ -0,0 +1,127 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /** + * Convert binary data to and from UrlBase64 encoding. This is identical to + * Base64 encoding, except that the padding character is "." and the other + * non-alphanumeric characters are "-" and "_" instead of "+" and "/". + *

    + * The purpose of UrlBase64 encoding is to provide a compact encoding of binary + * data that is safe for use as an URL parameter. Base64 encoding does not + * produce encoded values that are safe for use in URLs, since "/" can be + * interpreted as a path delimiter; "+" is the encoded form of a space; and + * "=" is used to separate a name from the corresponding value in an URL + * parameter. + *

    + */ + public class UrlBase64 + { + private static readonly IEncoder encoder = new UrlBase64Encoder(); + + /** + * Encode the input data producing a URL safe base 64 encoded byte array. + * + * @return a byte array containing the URL safe base 64 encoded data. + */ + public static byte[] Encode( + byte[] data) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.Encode(data, 0, data.Length, bOut); + } + catch (IOException e) + { + throw new Exception("exception encoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * Encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int Encode( + byte[] data, + Stream outStr) + { + return encoder.Encode(data, 0, data.Length, outStr); + } + + /** + * Decode the URL safe base 64 encoded input data - white space will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + byte[] data) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.Decode(data, 0, data.Length, bOut); + } + catch (IOException e) + { + throw new Exception("exception decoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * decode the URL safe base 64 encoded byte data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int Decode( + byte[] data, + Stream outStr) + { + return encoder.Decode(data, 0, data.Length, outStr); + } + + /** + * decode the URL safe base 64 encoded string data - whitespace will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + string data) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.DecodeString(data, bOut); + } + catch (IOException e) + { + throw new Exception("exception decoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * Decode the URL safe base 64 encoded string data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int Decode( + string data, + Stream outStr) + { + return encoder.DecodeString(data, outStr); + } + } +} diff --git a/src/core/srcbc/util/encoders/UrlBase64Encoder.cs b/src/core/srcbc/util/encoders/UrlBase64Encoder.cs new file mode 100644 index 0000000..99c9c9c --- /dev/null +++ b/src/core/srcbc/util/encoders/UrlBase64Encoder.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /** + * Convert binary data to and from UrlBase64 encoding. This is identical to + * Base64 encoding, except that the padding character is "." and the other + * non-alphanumeric characters are "-" and "_" instead of "+" and "/". + *

    + * The purpose of UrlBase64 encoding is to provide a compact encoding of binary + * data that is safe for use as an URL parameter. Base64 encoding does not + * produce encoded values that are safe for use in URLs, since "/" can be + * interpreted as a path delimiter; "+" is the encoded form of a space; and + * "=" is used to separate a name from the corresponding value in an URL + * parameter. + *

    + */ + public class UrlBase64Encoder + : Base64Encoder + { + public UrlBase64Encoder() + { + encodingTable[encodingTable.Length - 2] = (byte) '-'; + encodingTable[encodingTable.Length - 1] = (byte) '_'; + padding = (byte) '.'; + // we must re-create the decoding table with the new encoded values. + InitialiseDecodingTable(); + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/io/BaseInputStream.cs b/src/core/srcbc/util/io/BaseInputStream.cs new file mode 100644 index 0000000..4c28e13 --- /dev/null +++ b/src/core/srcbc/util/io/BaseInputStream.cs @@ -0,0 +1,47 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public abstract class BaseInputStream : Stream + { + private bool closed; + + public sealed override bool CanRead { get { return !closed; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return false; } } + public override void Close() { closed = true; } + public sealed override void Flush() {} + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + int pos = offset; + try + { + int end = offset + count; + while (pos < end) + { + int b = ReadByte(); + if (b == -1) break; + buffer[pos++] = (byte) b; + } + } + catch (IOException) + { + if (pos == offset) throw; + } + return pos - offset; + } + + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + } +} diff --git a/src/core/srcbc/util/io/BaseOutputStream.cs b/src/core/srcbc/util/io/BaseOutputStream.cs new file mode 100644 index 0000000..339eef4 --- /dev/null +++ b/src/core/srcbc/util/io/BaseOutputStream.cs @@ -0,0 +1,47 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public abstract class BaseOutputStream : Stream + { + private bool closed; + + public sealed override bool CanRead { get { return false; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return !closed; } } + public override void Close() { closed = true; } + public override void Flush() {} + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + + public override void Write(byte[] buffer, int offset, int count) + { + Debug.Assert(buffer != null); + Debug.Assert(0 <= offset && offset <= buffer.Length); + Debug.Assert(count >= 0); + + int end = offset + count; + + Debug.Assert(0 <= end && end <= buffer.Length); + + for (int i = offset; i < end; ++i) + { + this.WriteByte(buffer[i]); + } + } + + public virtual void Write(params byte[] buffer) + { + Write(buffer, 0, buffer.Length); + } + } +} diff --git a/src/core/srcbc/util/io/PushbackStream.cs b/src/core/srcbc/util/io/PushbackStream.cs new file mode 100644 index 0000000..ce34e79 --- /dev/null +++ b/src/core/srcbc/util/io/PushbackStream.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.Utilities; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class PushbackStream + : FilterStream + { + private int buf = -1; + + public PushbackStream( + Stream s) + : base(s) + { + } + + public override int ReadByte() + { + if (buf != -1) + { + int tmp = buf; + buf = -1; + return tmp; + } + + return base.ReadByte(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (buf != -1 && count > 0) + { + // TODO Can this case be made more efficient? + buffer[offset] = (byte) buf; + buf = -1; + return 1; + } + + return base.Read(buffer, offset, count); + } + + public virtual void Unread(int b) + { + if (buf != -1) + throw new InvalidOperationException("Can only push back one byte"); + + buf = b & 0xFF; + } + } +} diff --git a/src/core/srcbc/util/io/Streams.cs b/src/core/srcbc/util/io/Streams.cs new file mode 100644 index 0000000..38f2633 --- /dev/null +++ b/src/core/srcbc/util/io/Streams.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public sealed class Streams + { + private const int BufferSize = 512; + + private Streams() + { + } + + public static void Drain(Stream inStr) + { + byte[] bs = new byte[BufferSize]; + while (inStr.Read(bs, 0, bs.Length) > 0) + { + } + } + + public static byte[] ReadAll(Stream inStr) + { + MemoryStream buf = new MemoryStream(); + PipeAll(inStr, buf); + return buf.ToArray(); + } + + public static int ReadFully(Stream inStr, byte[] buf) + { + return ReadFully(inStr, buf, 0, buf.Length); + } + + public static int ReadFully(Stream inStr, byte[] buf, int off, int len) + { + int totalRead = 0; + while (totalRead < len) + { + int numRead = inStr.Read(buf, off + totalRead, len - totalRead); + if (numRead < 1) + break; + totalRead += numRead; + } + return totalRead; + } + + public static void PipeAll(Stream inStr, Stream outStr) + { + byte[] bs = new byte[BufferSize]; + int numRead; + while ((numRead = inStr.Read(bs, 0, bs.Length)) > 0) + { + outStr.Write(bs, 0, numRead); + } + } + } +} diff --git a/src/core/srcbc/util/net/IPAddress.cs b/src/core/srcbc/util/net/IPAddress.cs new file mode 100644 index 0000000..08976f6 --- /dev/null +++ b/src/core/srcbc/util/net/IPAddress.cs @@ -0,0 +1,112 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Utilities.Net +{ + public class IPAddress + { + /** + * Validate the given IPv4 or IPv6 address. + * + * @param address the IP address as a string. + * + * @return true if a valid address, false otherwise + */ + public static bool IsValid( + string address) + { + return IsValidIPv4(address) || IsValidIPv6(address); + } + + /** + * Validate the given IPv4 address. + * + * @param address the IP address as a string. + * + * @return true if a valid IPv4 address, false otherwise + */ + private static bool IsValidIPv4( + string address) + { + if (address.Length == 0) + return false; + + BigInteger octet; + int octets = 0; + + string temp = address + "."; + + int pos; + int start = 0; + while (start < temp.Length + && (pos = temp.IndexOf('.', start)) > start) + { + if (octets == 4) + return false; + + try + { + octet = new BigInteger(temp.Substring(start, pos - start)); + } + catch (FormatException) + { + return false; + } + + if (octet.SignValue < 0 || octet.BitLength > 8) + return false; + + start = pos + 1; + ++octets; + } + + return octets == 4; + } + + /** + * Validate the given IPv6 address. + * + * @param address the IP address as a string. + * + * @return true if a valid IPv4 address, false otherwise + */ + private static bool IsValidIPv6( + string address) + { + if (address.Length == 0) + return false; + + BigInteger octet; + int octets = 0; + + string temp = address + ":"; + + int pos; + int start = 0; + while (start < temp.Length + && (pos = temp.IndexOf(':', start)) > start) + { + if (octets == 8) + return false; + + try + { + octet = new BigInteger(temp.Substring(start, pos - start), 16); + } + catch (FormatException) + { + return false; + } + + if (octet.SignValue < 0 || octet.BitLength > 16) + return false; + + start = pos + 1; + octets++; + } + + return octets == 8; + } + } +} diff --git a/src/core/srcbc/util/zlib/Adler32.cs b/src/core/srcbc/util/zlib/Adler32.cs new file mode 100644 index 0000000..4926360 --- /dev/null +++ b/src/core/srcbc/util/zlib/Adler32.cs @@ -0,0 +1,88 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1.1.1 2007/01/24 16:41:26 psoares33 Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class Adler32{ + + // largest prime smaller than 65536 + private const int BASE=65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX=5552; + + internal long adler32(long adler, byte[] buf, int index, int len){ + if(buf == null){ return 1L; } + + long s1=adler&0xffff; + long s2=(adler>>16)&0xffff; + int k; + + while(len > 0) { + k=len=16){ + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + k-=16; + } + if(k!=0){ + do{ + s1+=buf[index++]&0xff; s2+=s1; + } + while(--k!=0); + } + s1%=BASE; + s2%=BASE; + } + return (s2<<16)|s1; + } + + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/zlib/Deflate.cs b/src/core/srcbc/util/zlib/Deflate.cs new file mode 100644 index 0000000..d3c3143 --- /dev/null +++ b/src/core/srcbc/util/zlib/Deflate.cs @@ -0,0 +1,1640 @@ +using System; +/* + * $Id: Deflate.cs,v 1.2 2008/05/12 15:24:47 psoares33 Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + public sealed class Deflate{ + + private const int MAX_MEM_LEVEL=9; + + private const int Z_DEFAULT_COMPRESSION=-1; + + private const int MAX_WBITS=15; // 32K LZ77 window + private const int DEF_MEM_LEVEL=8; + + internal class Config{ + internal int good_length; // reduce lazy search above this match length + internal int max_lazy; // do not perform lazy search above this match length + internal int nice_length; // quit search above this match length + internal int max_chain; + internal int func; + internal Config(int good_length, int max_lazy, + int nice_length, int max_chain, int func){ + this.good_length=good_length; + this.max_lazy=max_lazy; + this.nice_length=nice_length; + this.max_chain=max_chain; + this.func=func; + } + } + + private const int STORED=0; + private const int FAST=1; + private const int SLOW=2; + private static readonly Config[] config_table; + + static Deflate(){ + config_table=new Config[10]; + // good lazy nice chain + config_table[0]=new Config(0, 0, 0, 0, STORED); + config_table[1]=new Config(4, 4, 8, 4, FAST); + config_table[2]=new Config(4, 5, 16, 8, FAST); + config_table[3]=new Config(4, 6, 32, 32, FAST); + + config_table[4]=new Config(4, 4, 16, 16, SLOW); + config_table[5]=new Config(8, 16, 32, 32, SLOW); + config_table[6]=new Config(8, 16, 128, 128, SLOW); + config_table[7]=new Config(8, 32, 128, 256, SLOW); + config_table[8]=new Config(32, 128, 258, 1024, SLOW); + config_table[9]=new Config(32, 258, 258, 4096, SLOW); + } + + private static readonly String[] z_errmsg = { + "need dictionary", // Z_NEED_DICT 2 + "stream end", // Z_STREAM_END 1 + "", // Z_OK 0 + "file error", // Z_ERRNO (-1) + "stream error", // Z_STREAM_ERROR (-2) + "data error", // Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + // block not completed, need more input or more output + private const int NeedMore=0; + + // block flush performed + private const int BlockDone=1; + + // finish started, need only more output at next deflate + private const int FinishStarted=2; + + // finish done, accept no more input or output + private const int FinishDone=3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT=0x20; + + private const int Z_FILTERED=1; + private const int Z_HUFFMAN_ONLY=2; + private const int Z_DEFAULT_STRATEGY=0; + + private const int Z_NO_FLUSH=0; + private const int Z_PARTIAL_FLUSH=1; + private const int Z_SYNC_FLUSH=2; + private const int Z_FULL_FLUSH=3; + private const int Z_FINISH=4; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int INIT_STATE=42; + private const int BUSY_STATE=113; + private const int FINISH_STATE=666; + + // The deflate compression method + private const int Z_DEFLATED=8; + + private const int STORED_BLOCK=0; + private const int STATIC_TREES=1; + private const int DYN_TREES=2; + + // The three kinds of block type + private const int Z_BINARY=0; + private const int Z_ASCII=1; + private const int Z_UNKNOWN=2; + + private const int Buf_size=8*2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6=16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10=17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138=18; + + private const int MIN_MATCH=3; + private const int MAX_MATCH=258; + private const int MIN_LOOKAHEAD=(MAX_MATCH+MIN_MATCH+1); + + private const int MAX_BITS=15; + private const int D_CODES=30; + private const int BL_CODES=19; + private const int LENGTH_CODES=29; + private const int LITERALS=256; + private const int L_CODES=(LITERALS+1+LENGTH_CODES); + private const int HEAP_SIZE=(2*L_CODES+1); + + private const int END_BLOCK=256; + + internal ZStream strm; // pointer back to this zlib stream + internal int status; // as the name implies + internal byte[] pending_buf; // output still pending + internal int pending_buf_size; // size of pending_buf + internal int pending_out; // next pending byte to output to the stream + internal int pending; // nb of bytes in the pending buffer + internal int noheader; // suppress zlib header and adler32 + internal byte data_type; // UNKNOWN, BINARY or ASCII + internal byte method; // STORED (for zip only) or DEFLATED + internal int last_flush; // value of flush param for previous deflate call + + internal int w_size; // LZ77 window size (32K by default) + internal int w_bits; // log2(w_size) (8..16) + internal int w_mask; // w_size - 1 + + internal byte[] window; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least wSize + // bytes. With this organization, matches are limited to a distance of + // wSize-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: use the user input buffer as sliding window. + + internal int window_size; + // Actual size of window: 2*wSize, except when the user input buffer + // is directly used as sliding window. + + internal short[] prev; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + + internal short[] head; // Heads of the hash chains or NIL. + + internal int ins_h; // hash index of string to be inserted + internal int hash_size; // number of elements in hash table + internal int hash_bits; // log2(hash_size) + internal int hash_mask; // hash_size-1 + + // Number of bits by which ins_h must be shifted at each input + // step. It must be such that after MIN_MATCH steps, the oldest + // byte no longer takes part in the hash key, that is: + // hash_shift * MIN_MATCH >= hash_bits + internal int hash_shift; + + // Window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + internal int block_start; + + internal int match_length; // length of best match + internal int prev_match; // previous match + internal int match_available; // set if previous match exists + internal int strstart; // start of string to insert + internal int match_start; // start of matching string + internal int lookahead; // number of valid bytes ahead in window + + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + internal int prev_length; + + // To speed up deflation, hash chains are never searched beyond this + // length. A higher limit improves compression ratio but degrades the speed. + internal int max_chain_length; + + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + internal int max_lazy_match; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + internal int level; // compression level (1..9) + internal int strategy; // favor or force Huffman coding + + // Use a faster search when the previous match is longer than this + internal int good_match; + + // Stop searching when current match exceeds this + internal int nice_match; + + internal short[] dyn_ltree; // literal and length tree + internal short[] dyn_dtree; // distance tree + internal short[] bl_tree; // Huffman tree for bit lengths + + internal Tree l_desc=new Tree(); // desc for literal tree + internal Tree d_desc=new Tree(); // desc for distance tree + internal Tree bl_desc=new Tree(); // desc for bit length tree + + // number of codes at each bit length for an optimal tree + internal short[] bl_count=new short[MAX_BITS+1]; + + // heap used to build the Huffman trees + internal int[] heap=new int[2*L_CODES+1]; + + internal int heap_len; // number of elements in the heap + internal int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + // Depth of each subtree used as tie breaker for trees of equal frequency + internal byte[] depth=new byte[2*L_CODES+1]; + + internal int l_buf; // index for literals or lengths */ + + // Size of match buffer for literals/lengths. There are 4 reasons for + // limiting lit_bufsize to 64K: + // - frequencies can be kept in 16 bit counters + // - if compression is not successful for the first block, all input + // data is still in the window so we can still emit a stored block even + // when input comes from standard input. (This can also be done for + // all blocks if lit_bufsize is not greater than 32K.) + // - if compression is not successful for a file smaller than 64K, we can + // even emit a stored file instead of a stored block (saving 5 bytes). + // This is applicable only for zip (not gzip or zlib). + // - creating new Huffman trees less frequently may not provide fast + // adaptation to changes in the input data statistics. (Take for + // example a binary file with poorly compressible code followed by + // a highly compressible string table.) Smaller buffer sizes give + // fast adaptation but have of course the overhead of transmitting + // trees more frequently. + // - I can't count above 4 + internal int lit_bufsize; + + internal int last_lit; // running index in l_buf + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + internal int d_buf; // index of pendig_buf + + internal int opt_len; // bit length of current block with optimal trees + internal int static_len; // bit length of current block with static trees + internal int matches; // number of string matches in current block + internal int last_eob_len; // bit length of EOB code for last block + + // Output buffer. bits are inserted starting at the bottom (least + // significant bits). + internal uint bi_buf; + + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + internal int bi_valid; + + internal Deflate(){ + dyn_ltree=new short[HEAP_SIZE*2]; + dyn_dtree=new short[(2*D_CODES+1)*2]; // distance tree + bl_tree=new short[(2*BL_CODES+1)*2]; // Huffman tree for bit lengths + } + + internal void lm_init() { + window_size=2*w_size; + + head[hash_size-1]=0; + for(int i=0; i= 3; max_blindex--) { + if (bl_tree[Tree.bl_order[max_blindex]*2+1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3*(max_blindex+1) + 5+5+4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + internal void send_all_trees(int lcodes, int dcodes, int blcodes){ + int rank; // index in bl_order + + send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) { + send_bits(bl_tree[Tree.bl_order[rank]*2+1], 3); + } + send_tree(dyn_ltree, lcodes-1); // literal tree + send_tree(dyn_dtree, dcodes-1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + internal void send_tree (short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ){ + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0*2+1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0){ max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[(n+1)*2+1]; + if(++count < max_count && curlen == nextlen) { + continue; + } + else if(count < min_count) { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if(curlen != 0){ + if(curlen != prevlen){ + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count-3, 2); + } + else if(count <= 10){ + send_code(REPZ_3_10, bl_tree); + send_bits(count-3, 3); + } + else{ + send_code(REPZ_11_138, bl_tree); + send_bits(count-11, 7); + } + count = 0; prevlen = curlen; + if(nextlen == 0){ + max_count = 138; min_count = 3; + } + else if(curlen == nextlen){ + max_count = 6; min_count = 3; + } + else{ + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + internal void put_byte(byte[] p, int start, int len){ + System.Array.Copy(p, start, pending_buf, pending, len); + pending+=len; + } + + internal void put_byte(byte c){ + pending_buf[pending++]=c; + } + internal void put_short(int w) { + pending_buf[pending++]=(byte)(w/*&0xff*/); + pending_buf[pending++]=(byte)(w>>8); + } + internal void putShortMSB(int b){ + pending_buf[pending++]=(byte)(b>>8); + pending_buf[pending++]=(byte)(b/*&0xff*/); + } + + internal void send_code(int c, short[] tree){ + int c2=c*2; + send_bits((tree[c2]&0xffff), (tree[c2+1]&0xffff)); + } + + internal void send_bits(int val, int length){ + if (bi_valid > Buf_size - length) { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++]=(byte)(bi_buf/*&0xff*/); + pending_buf[pending++]=(byte)(bi_buf>>8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } else { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } +// int len = length; +// if (bi_valid > (int)Buf_size - len) { +// int val = value; +// // bi_buf |= (val << bi_valid); +// bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); +// put_short(bi_buf); +// bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); +// bi_valid += len - Buf_size; +// } else { +// // bi_buf |= (value) << bi_valid; +// bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); +// bi_valid += len; +// } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + internal void _tr_align(){ + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) { + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + internal bool _tr_tally (int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ){ + + pending_buf[d_buf+last_lit*2] = (byte)(dist>>8); + pending_buf[d_buf+last_lit*2+1] = (byte)dist; + + pending_buf[l_buf+last_lit] = (byte)lc; last_lit++; + + if (dist == 0) { + // lc is the unmatched char + dyn_ltree[lc*2]++; + } + else { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Tree._length_code[lc]+LITERALS+1)*2]++; + dyn_dtree[Tree.d_code(dist)*2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > 2) { + // Compute an upper bound for the compressed length + int out_length = last_lit*8; + int in_length = strstart - block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (int)((int)dyn_dtree[dcode*2] * + (5L+Tree.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit/2)) && out_length < in_length/2) return true; + } + + return (last_lit == lit_bufsize-1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + internal void compress_block(short[] ltree, short[] dtree){ + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0){ + do{ + dist=((pending_buf[d_buf+lx*2]<<8)&0xff00)| + (pending_buf[d_buf+lx*2+1]&0xff); + lc=(pending_buf[l_buf+lx])&0xff; lx++; + + if(dist == 0){ + send_code(lc, ltree); // send a literal byte + } + else{ + // Here, lc is the match length - MIN_MATCH + code = Tree._length_code[lc]; + + send_code(code+LITERALS+1, ltree); // send the length code + extra = Tree.extra_lbits[code]; + if(extra != 0){ + lc -= Tree.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.d_code(dist); + + send_code(code, dtree); // send the distance code + extra = Tree.extra_dbits[code]; + if (extra != 0) { + dist -= Tree.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK*2+1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + internal void set_data_type(){ + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while(n<7){ bin_freq += dyn_ltree[n*2]; n++;} + while(n<128){ ascii_freq += dyn_ltree[n*2]; n++;} + while(n (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + internal void bi_flush(){ + if (bi_valid == 16) { + pending_buf[pending++]=(byte)(bi_buf/*&0xff*/); + pending_buf[pending++]=(byte)(bi_buf>>8); + bi_buf=0; + bi_valid=0; + } + else if (bi_valid >= 8) { + pending_buf[pending++]=(byte)(bi_buf); + bi_buf>>=8; + bi_buf &= 0x00ff; + bi_valid-=8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + internal void bi_windup(){ + if (bi_valid > 8) { + pending_buf[pending++]=(byte)(bi_buf); + pending_buf[pending++]=(byte)(bi_buf>>8); + } else if (bi_valid > 0) { + pending_buf[pending++]=(byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + internal void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ){ + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(window, buf, len); + } + + internal void flush_block_only(bool eof){ + _tr_flush_block(block_start>=0 ? block_start : -1, + strstart-block_start, + eof); + block_start=strstart; + strm.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + internal int deflate_stored(int flush){ + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if(max_block_size > pending_buf_size - 5) { + max_block_size = pending_buf_size - 5; + } + + // Copy as much as possible from input to output: + while(true){ + // Fill the window as much as possible: + if(lookahead<=1){ + fill_window(); + if(lookahead==0 && flush==Z_NO_FLUSH) return NeedMore; + if(lookahead==0) break; // flush the current block + } + + strstart+=lookahead; + lookahead=0; + + // Emit a stored block if pending_buf will be full: + max_start=block_start+max_block_size; + if(strstart==0|| strstart>=max_start) { + // strstart == 0 is possible when wraparound on 16-bit machine + lookahead = (int)(strstart-max_start); + strstart = (int)max_start; + + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if(strstart-block_start >= w_size-MIN_LOOKAHEAD) { + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + } + } + + flush_block_only(flush == Z_FINISH); + if(strm.avail_out==0) + return (flush == Z_FINISH) ? FinishStarted : NeedMore; + + return flush == Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + internal void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ){ + send_bits((STORED_BLOCK<<1)+(eof?1:0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + internal void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if(level > 0) { + // Check if the file is ascii or binary + if(data_type == Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.build_tree(this); + + d_desc.build_tree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex=build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb=(opt_len+3+7)>>3; + static_lenb=(static_len+3+7)>>3; + + if(static_lenb<=opt_lenb) opt_lenb=static_lenb; + } + else { + opt_lenb=static_lenb=stored_len+5; // force a stored block + } + + if(stored_len+4<=opt_lenb && buf != -1){ + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if(static_lenb == opt_lenb){ + send_bits((STATIC_TREES<<1)+(eof?1:0), 3); + compress_block(StaticTree.static_ltree, StaticTree.static_dtree); + } + else{ + send_bits((DYN_TREES<<1)+(eof?1:0), 3); + send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if(eof){ + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + internal void fill_window(){ + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do{ + more = (window_size-lookahead-strstart); + + // Deal with !@#$% 64K limit: + if(more==0 && strstart==0 && lookahead==0){ + more = w_size; + } + else if(more==-1) { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if(strstart >= w_size+ w_size-MIN_LOOKAHEAD) { + System.Array.Copy(window, w_size, window, 0, w_size); + match_start-=w_size; + strstart-=w_size; // we now have strstart >= MAX_DIST + block_start-=w_size; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = hash_size; + p=n; + do { + m = (head[--p]&0xffff); + head[p]=(short)(m>=w_size ? (m-w_size) : 0); + } + while (--n != 0); + + n = w_size; + p = n; + do { + m = (prev[--p]&0xffff); + prev[p] = (short)(m >= w_size ? (m-w_size) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n!=0); + more += w_size; + } + + if (strm.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = strm.read_buf(window, strstart + lookahead, more); + lookahead += n; + + // Initialize the hash value now that we have some input: + if(lookahead >= MIN_MATCH) { + ins_h = window[strstart]&0xff; + ins_h=(((ins_h)<= MIN_MATCH){ + ins_h=(((ins_h)<=MIN_MATCH){ + // check_match(strstart, match_start, match_length); + + bflush=_tr_tally(strstart-match_start, match_length-MIN_MATCH); + + lookahead -= match_length; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if(match_length <= max_lazy_match && + lookahead >= MIN_MATCH) { + match_length--; // string at strstart already in hash table + do{ + strstart++; + + ins_h=((ins_h<= MIN_MATCH) { + ins_h=(((ins_h)< 4096))) { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + match_length = MIN_MATCH-1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if(prev_length >= MIN_MATCH && match_length <= prev_length) { + int max_insert = strstart + lookahead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush=_tr_tally(strstart-1-prev_match, prev_length - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + lookahead -= prev_length-1; + prev_length -= 2; + do{ + if(++strstart <= max_insert) { + ins_h=(((ins_h)<(w_size-MIN_LOOKAHEAD) ? + strstart-(w_size-MIN_LOOKAHEAD) : 0; + int nice_match=this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = w_mask; + + int strend = strstart + MAX_MATCH; + byte scan_end1 = window[scan+best_len-1]; + byte scan_end = window[scan+best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (prev_length >= good_match) { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > lookahead) nice_match = lookahead; + + do { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (window[match+best_len] != scan_end || + window[match+best_len-1] != scan_end1 || + window[match] != window[scan] || + window[++match] != window[scan+1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do { + } while (window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if(len>best_len) { + match_start = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = window[scan+best_len-1]; + scan_end = window[scan+best_len]; + } + + } while ((cur_match = (prev[cur_match & wmask]&0xffff)) > limit + && --chain_length != 0); + + if (best_len <= lookahead) return best_len; + return lookahead; + } + + internal int deflateInit(ZStream strm, int level, int bits){ + return deflateInit2(strm, level, Z_DEFLATED, bits, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY); + } + internal int deflateInit(ZStream strm, int level){ + return deflateInit(strm, level, MAX_WBITS); + } + internal int deflateInit2(ZStream strm, int level, int method, int windowBits, + int memLevel, int strategy){ + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + strm.msg = null; + + if (level == Z_DEFAULT_COMPRESSION) level = 6; + + if (windowBits < 0) { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + strm.dstate = (Deflate)this; + + this.noheader = noheader; + w_bits = windowBits; + w_size = 1 << w_bits; + w_mask = w_size - 1; + + hash_bits = memLevel + 7; + hash_size = 1 << hash_bits; + hash_mask = hash_size - 1; + hash_shift = ((hash_bits+MIN_MATCH-1)/MIN_MATCH); + + window = new byte[w_size*2]; + prev = new short[w_size]; + head = new short[hash_size]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize*4]; + pending_buf_size = lit_bufsize*4; + + d_buf = lit_bufsize/2; + l_buf = (1+2)*lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this.method = (byte)method; + + return deflateReset(strm); + } + + internal int deflateReset(ZStream strm){ + strm.total_in = strm.total_out = 0; + strm.msg = null; // + strm.data_type = Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if(noheader < 0) { + noheader = 0; // was set to -1 by deflate(..., Z_FINISH); + } + status = (noheader!=0) ? BUSY_STATE : INIT_STATE; + strm.adler=strm._adler.adler32(0, null, 0, 0); + + last_flush = Z_NO_FLUSH; + + tr_init(); + lm_init(); + return Z_OK; + } + + internal int deflateEnd(){ + if(status!=INIT_STATE && status!=BUSY_STATE && status!=FINISH_STATE){ + return Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf=null; + head=null; + prev=null; + window=null; + // free + // dstate=null; + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; + } + + internal int deflateParams(ZStream strm, int _level, int _strategy){ + int err=Z_OK; + + if(_level == Z_DEFAULT_COMPRESSION){ + _level = 6; + } + if(_level < 0 || _level > 9 || + _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + if(config_table[level].func!=config_table[_level].func && + strm.total_in != 0) { + // Flush the last buffer: + err = strm.deflate(Z_PARTIAL_FLUSH); + } + + if(level != _level) { + level = _level; + max_lazy_match = config_table[level].max_lazy; + good_match = config_table[level].good_length; + nice_match = config_table[level].nice_length; + max_chain_length = config_table[level].max_chain; + } + strategy = _strategy; + return err; + } + + internal int deflateSetDictionary (ZStream strm, byte[] dictionary, int dictLength){ + int length = dictLength; + int index=0; + + if(dictionary == null || status != INIT_STATE) + return Z_STREAM_ERROR; + + strm.adler=strm._adler.adler32(strm.adler, dictionary, 0, dictLength); + + if(length < MIN_MATCH) return Z_OK; + if(length > w_size-MIN_LOOKAHEAD){ + length = w_size-MIN_LOOKAHEAD; + index=dictLength-length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, window, 0, length); + strstart = length; + block_start = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + ins_h = window[0]&0xff; + ins_h=(((ins_h)<Z_FINISH || flush<0){ + return Z_STREAM_ERROR; + } + + if(strm.next_out == null || + (strm.next_in == null && strm.avail_in != 0) || + (status == FINISH_STATE && flush != Z_FINISH)) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_STREAM_ERROR)]; + return Z_STREAM_ERROR; + } + if(strm.avail_out == 0){ + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + this.strm = strm; // just in case + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if(status == INIT_STATE) { + int header = (Z_DEFLATED+((w_bits-8)<<4))<<8; + int level_flags=((level-1)&0xff)>>1; + + if(level_flags>3) level_flags=3; + header |= (level_flags<<6); + if(strstart!=0) header |= PRESET_DICT; + header+=31-(header % 31); + + status=BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if(strstart!=0){ + putShortMSB((int)(strm.adler>>16)); + putShortMSB((int)(strm.adler&0xffff)); + } + strm.adler=strm._adler.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if(pending != 0) { + strm.flush_pending(); + if(strm.avail_out == 0) { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = -1; + return Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if(strm.avail_in==0 && flush <= old_flush && + flush != Z_FINISH) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if(status == FINISH_STATE && strm.avail_in != 0) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if(strm.avail_in!=0 || lookahead!=0 || + (flush != Z_NO_FLUSH && status != FINISH_STATE)) { + int bstate=-1; + switch(config_table[level].func){ + case STORED: + bstate = deflate_stored(flush); + break; + case FAST: + bstate = deflate_fast(flush); + break; + case SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate==FinishStarted || bstate==FinishDone) { + status = FINISH_STATE; + } + if (bstate==NeedMore || bstate==FinishStarted) { + if(strm.avail_out == 0) { + last_flush = -1; // avoid BUF_ERROR next call, see above + } + return Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate==BlockDone) { + if(flush == Z_PARTIAL_FLUSH) { + _tr_align(); + } + else { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if(flush == Z_FULL_FLUSH) { + //state.head[s.hash_size-1]=0; + for(int i=0; i>16)); + putShortMSB((int)(strm.adler&0xffff)); + strm.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? Z_OK : Z_STREAM_END; + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/zlib/InfBlocks.cs b/src/core/srcbc/util/zlib/InfBlocks.cs new file mode 100644 index 0000000..333c701 --- /dev/null +++ b/src/core/srcbc/util/zlib/InfBlocks.cs @@ -0,0 +1,618 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008/05/12 15:24:47 psoares33 Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfBlocks{ + private const int MANY=1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int TYPE=0; // get type bits (3, including end bit) + private const int LENS=1; // get lengths for stored + private const int STORED=2;// processing stored block + private const int TABLE=3; // get table lengths + private const int BTREE=4; // get bit lengths tree for a dynamic block + private const int DTREE=5; // get length, distance trees for a dynamic block + private const int CODES=6; // processing fixed or dynamic block + private const int DRY=7; // output remaining window bytes + private const int DONE=8; // finished last block, done + private const int BAD=9; // ot a data error--stuck here + + internal int mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb=new int[1]; // bit length tree depth + internal int[] tb=new int[1]; // bit length decoding tree + + internal InfCodes codes=new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InfTree inftree=new InfTree(); + + internal InfBlocks(ZStream z, Object checkfn, int w){ + hufts=new int[MANY*3]; + window=new byte[w]; + end=w; + this.checkfn = checkfn; + mode = TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c){ + if(c!=null) c[0]=check; + if(mode==BTREE || mode==DTREE){ + } + if(mode==CODES){ + codes.free(z); + } + mode=TYPE; + bitk=0; + bitb=0; + read=write=0; + + if(checkfn != null) + z.adler=check=z._adler.adler32(0L, null, 0, 0); + } + + internal int proc(ZStream z, int r){ + int t; // temporary storage + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=bitb;k=bitk;} { + q=write;m=(int)(q> 1){ + case 0: { // stored + b>>=(3);k-=(3);} + t = k & 7; { // go to byte boundary + + b>>=(t);k-=(t);} + mode = LENS; // get length of stored block + break; + case 1: { // fixed + int[] bl=new int[1]; + int[] bd=new int[1]; + int[][] tl=new int[1][]; + int[][] td=new int[1][]; + + InfTree.inflate_trees_fixed(bl, bd, tl, td, z); + codes.init(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } { + + b>>=(3);k-=(3);} + + mode = CODES; + break; + case 2: { // dynamic + + b>>=(3);k-=(3);} + + mode = TABLE; + break; + case 3: { // illegal + + b>>=(3);k-=(3);} + mode = BAD; + z.msg = "invalid block type"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + break; + case LENS: + + while(k<(32)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<> 16) & 0xffff) != (b & 0xffff)){ + mode = BAD; + z.msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left!=0 ? STORED : (last!=0 ? DRY : TYPE); + break; + case STORED: + if (n == 0){ + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + if(m==0){ + if(q==end&&read!=0){ + q=0; m=(int)(qn) t = n; + if(t>m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last!=0 ? DRY : TYPE; + break; + case TABLE: + + while(k<(14)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)< 29 || ((t >> 5) & 0x1f) > 29) { + mode = BAD; + z.msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if(blens==null || blens.Length>=(14);k-=(14);} + + index = 0; + mode = BTREE; + goto case BTREE; + case BTREE: + while (index < 4 + (table >> 10)){ + while(k<(3)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>=(3);k-=(3);} + } + + while(index < 19){ + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); + if (t != Z_OK){ + r = t; + if (r == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + index = 0; + mode = DTREE; + goto case DTREE; + case DTREE: + while (true){ + t = table; + if(!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))){ + break; + } + + int i, j, c; + + t = bb[0]; + + while(k<(t)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>=(t);k-=(t); + blens[index++] = c; + } + else { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while(k<(t+i)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>=(t);k-=(t); + + j += (b & inflate_mask[i]); + + b>>=(i);k-=(i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)){ + blens=null; + mode = BAD; + z.msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + c = c == 16 ? blens[i-1] : 0; + do{ + blens[i++] = c; + } + while (--j!=0); + index = i; + } + } + + tb[0]=-1; { + int[] bl=new int[1]; + int[] bd=new int[1]; + int[] tl=new int[1]; + int[] td=new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (t != Z_OK){ + if (t == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + r = t; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + codes.init(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = CODES; + goto case CODES; + case CODES: + bitb=b; bitk=k; + z.avail_in=n; z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + + if ((r = codes.proc(this, z, r)) != Z_STREAM_END){ + return inflate_flush(z, r); + } + r = Z_OK; + codes.free(z); + + p=z.next_in_index; n=z.avail_in;b=bitb;k=bitk; + q=write;m=(int)(q z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(checkfn != null) + z.adler=check=z._adler.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end){ + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(checkfn != null) + z.adler=check=z._adler.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/zlib/InfCodes.cs b/src/core/srcbc/util/zlib/InfCodes.cs new file mode 100644 index 0000000..a31d858 --- /dev/null +++ b/src/core/srcbc/util/zlib/InfCodes.cs @@ -0,0 +1,611 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008/05/12 15:24:47 psoares33 Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfCodes{ + + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + private const int START=0; // x: set up for LEN + private const int LEN=1; // i: get length/literal/eob next + private const int LENEXT=2; // i: getting length extra (have base) + private const int DIST=3; // i: get distance next + private const int DISTEXT=4;// i: getting distance extra + private const int COPY=5; // o: copying bytes in window, waiting for space + private const int LIT=6; // o: got literal, waiting for output space + private const int WASH=7; // o: got eob, possibly still output waiting + private const int END=8; // x: got eob and all data flushed + private const int BADCODE=9;// x: got error + + int mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index=0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InfCodes(){ + } + internal void init(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z){ + mode=START; + lbits=(byte)bl; + dbits=(byte)bd; + ltree=tl; + ltree_index=tl_index; + dtree = td; + dtree_index=td_index; + tree=null; + } + + internal int proc(InfBlocks s, ZStream z, int r){ + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b=0; // bit buffer + int k=0; // bits in bit buffer + int p=0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q= 258 && n >= 10){ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q>=(tree[tindex+1]); + k-=(tree[tindex+1]); + + e=tree[tindex]; + + if(e == 0){ // literal + lit = tree[tindex+2]; + mode = LIT; + break; + } + if((e & 16)!=0 ){ // length + get = e & 15; + len = tree[tindex+2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3+tree[tindex+2]; + break; + } + if ((e & 32)!=0){ // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + + case LENEXT: // i: getting length extra (have base) + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + need = dbits; + tree = dtree; + tree_index=dtree_index; + mode = DIST; + goto case DIST; + case DIST: // i: get distance next + j = need; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<>=tree[tindex+1]; + k-=tree[tindex+1]; + + e = (tree[tindex]); + if((e & 16)!=0){ // distance + get = e & 15; + dist = tree[tindex+2]; + mode = DISTEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3 + tree[tindex+2]; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid distance code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + + case DISTEXT: // i: getting distance extra + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + mode = COPY; + goto case COPY; + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while(f < 0){ // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len!=0){ + + if(m==0){ + if(q==s.end&&s.read!=0){q=0;m=q 7){ // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write=q; r=s.inflate_flush(z,r); + q=s.write;m=q= 258 && n >= 10 + // get literal/length code + while(k<(20)){ // max bits for literal/length code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++] = (byte)tp[tp_index_t_3+2]; + m--; + continue; + } + do { + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + e &= 15; + c = tp[tp_index_t_3+2] + ((int)b & inflate_mask[e]); + + b>>=e; k-=e; + + // decode distance base of block to copy + while(k<(15)){ // max bits for distance code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + // get extra bits to add to distance base + e &= 15; + while(k<(e)){ // get extra bits (up to 13) + n--; + b|=(z.next_in[p++]&0xff)<>=(e); k-=(e); + + // do the copy + m -= c; + if (q >= d){ // offset before dest + // just copy + r=q-d; + if(q-r>0 && 2>(q-r)){ + s.window[q++]=s.window[r++]; // minimum count is three, + s.window[q++]=s.window[r++]; // so unroll loop a little + c-=2; + } + else{ + System.Array.Copy(s.window, r, s.window, q, 2); + q+=2; r+=2; c-=2; + } + } + else{ // else offset after destination + r=q-d; + do{ + r+=s.end; // force pointer in window + }while(r<0); // covers invalid distances + e=s.end-r; + if(c>e){ // if source crosses, + c-=e; // wrapped copy + if(q-r>0 && e>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--e!=0); + } + else{ + System.Array.Copy(s.window, r, s.window, q, e); + q+=e; r+=e; e=0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if(q-r>0 && c>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--c!=0); + } + else{ + System.Array.Copy(s.window, r, s.window, q, c); + q+=c; r+=c; c=0; + } + break; + } + else if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + e=tp[tp_index_t_3]; + } + else{ + z.msg = "invalid distance code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + break; + } + + if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + if((e=tp[tp_index_t_3])==0){ + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++]=(byte)tp[tp_index_t_3+2]; + m--; + break; + } + } + else if((e&32)!=0){ + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_STREAM_END; + } + else{ + z.msg="invalid literal/length code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + } + while(m>=258 && n>= 10); + + // not enough input or output--restore pointers and return + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_OK; + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/zlib/InfTree.cs b/src/core/srcbc/util/zlib/InfTree.cs new file mode 100644 index 0000000..e7ce3bb --- /dev/null +++ b/src/core/srcbc/util/zlib/InfTree.cs @@ -0,0 +1,523 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008/05/12 15:24:47 psoares33 Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfTree{ + + private const int MANY=1440; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX=15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ){ + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do { + c[b[bindex+p]]++; p++; i--; // assume all entries <= BMAX + }while(i!=0); + + if(c[0] == n){ // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if(c[j]!=0) break; + k = j; // minimum code length + if(l < j){ + l = j; + } + for (i = BMAX; i!=0; i--){ + if(c[i]!=0) break; + } + g = i; // maximum code length + if(l > i){ + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1){ + if ((y -= c[j]) < 0){ + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0){ + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i!=0) { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do { + if ((j = b[bindex+p]) != 0){ + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++){ + a = c[k]; + while (a--!=0){ + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l){ + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if((f=1<<(j=k-w))>a+1){ // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if(j < z){ + while (++j < z){ // try smaller tables up to z bits + if((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY){ // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if(h!=0){ + x[h]=i; // save pattern for backing up + r[0]=(byte)j; // bits in this table + r[1]=(byte)l; // bits to dump before this table + j=i>>(w - l); + r[2] = (int)(q - u[h-1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h-1]+j)*3, 3); // connect to last table + } + else{ + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n){ + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s){ + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else{ + r[0]=(byte)(e[v[p]-s]+16+64); // non-simple--look up in lists + r[2]=d[v[p++] - s]; + } + + // fill code-like entries with r + f=1<<(k-w); + for (j=i>>w;j>= 1){ + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]){ + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + + internal int inflate_trees_bits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + initWorkArea(19); + hn[0]=0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if(result == Z_BUF_ERROR || bb[0] == 0){ + z.msg = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + internal int inflate_trees_dynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + + // build literal/length tree + initWorkArea(288); + hn[0]=0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0){ + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed literal/length tree"; + } + else if (result != Z_MEM_ERROR){ + z.msg = "incomplete literal/length tree"; + result = Z_DATA_ERROR; + } + return result; + } + + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)){ + if (result == Z_DATA_ERROR){ + z.msg = "oversubscribed distance tree"; + } + else if (result == Z_BUF_ERROR) { + z.msg = "incomplete distance tree"; + result = Z_DATA_ERROR; + } + else if (result != Z_MEM_ERROR){ + z.msg = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + } + + internal static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ){ + bl[0]=fixed_bl; + bd[0]=fixed_bd; + tl[0]=fixed_tl; + td[0]=fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize){ + if(hn==null){ + hn=new int[1]; + v=new int[vsize]; + c=new int[BMAX+1]; + r=new int[3]; + u=new int[BMAX]; + x=new int[BMAX+1]; + } + if(v.Lengthstate); + return Z_OK; + } + + internal int inflateInit(ZStream z, int w){ + z.msg = null; + blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + nowrap = 0; + if(w < 0){ + w = - w; + nowrap = 1; + } + + // set window size + if(w<8 ||w>15){ + inflateEnd(z); + return Z_STREAM_ERROR; + } + wbits=w; + + z.istate.blocks=new InfBlocks(z, + z.istate.nowrap!=0 ? null : this, + 1<>4)+8>z.istate.wbits){ + z.istate.mode = BAD; + z.msg="invalid window size"; + z.istate.marker = 5; // can't try inflateSync + break; + } + z.istate.mode=FLAG; + goto case FLAG; + case FLAG: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + b = (z.next_in[z.next_in_index++])&0xff; + + if((((z.istate.method << 8)+b) % 31)!=0){ + z.istate.mode = BAD; + z.msg = "incorrect header check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + if((b&PRESET_DICT)==0){ + z.istate.mode = BLOCKS; + break; + } + z.istate.mode = DICT4; + goto case DICT4; + case DICT4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + z.istate.mode=DICT3; + goto case DICT3; + case DICT3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + z.istate.mode=DICT2; + goto case DICT2; + case DICT2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + z.istate.mode=DICT1; + goto case DICT1; + case DICT1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need += (z.next_in[z.next_in_index++]&0xffL); + z.adler = z.istate.need; + z.istate.mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z.istate.mode = BAD; + z.msg = "need dictionary"; + z.istate.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case BLOCKS: + + r = z.istate.blocks.proc(z, r); + if(r == Z_DATA_ERROR){ + z.istate.mode = BAD; + z.istate.marker = 0; // can try inflateSync + break; + } + if(r == Z_OK){ + r = f; + } + if(r != Z_STREAM_END){ + return r; + } + r = f; + z.istate.blocks.reset(z, z.istate.was); + if(z.istate.nowrap!=0){ + z.istate.mode=DONE; + break; + } + z.istate.mode=CHECK4; + goto case CHECK4; + case CHECK4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + z.istate.mode=CHECK3; + goto case CHECK3; + case CHECK3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + z.istate.mode = CHECK2; + goto case CHECK2; + case CHECK2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + z.istate.mode = CHECK1; + goto case CHECK1; + case CHECK1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=(z.next_in[z.next_in_index++]&0xffL); + + if(((int)(z.istate.was[0])) != ((int)(z.istate.need))){ + z.istate.mode = BAD; + z.msg = "incorrect data check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + z.istate.mode = DONE; + goto case DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + } + } + + + internal int inflateSetDictionary(ZStream z, byte[] dictionary, int dictLength){ + int index=0; + int length = dictLength; + if(z==null || z.istate == null|| z.istate.mode != DICT0) + return Z_STREAM_ERROR; + + if(z._adler.adler32(1L, dictionary, 0, dictLength)!=z.adler){ + return Z_DATA_ERROR; + } + + z.adler = z._adler.adler32(0, null, 0, 0); + + if(length >= (1<>7)]); + } + + internal short[] dyn_tree; // the dynamic tree + internal int max_code; // largest code with non zero frequency + internal StaticTree stat_desc; // the corresponding static tree + + // Compute the optimal bit lengths for a tree and update the total bit length + // for the current block. + // IN assertion: the fields freq and dad are set, heap[heap_max] and + // above are the tree nodes sorted by increasing frequency. + // OUT assertions: the field len is set to the optimal bit length, the + // array bl_count contains the frequencies for each bit length. + // The length opt_len is updated; static_len is also updated if stree is + // not null. + internal void gen_bitlen(Deflate s){ + short[] tree = dyn_tree; + short[] stree = stat_desc.static_tree; + int[] extra = stat_desc.extra_bits; + int based = stat_desc.extra_base; + int max_length = stat_desc.max_length; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max]*2+1] = 0; // root of the heap + + for(h=s.heap_max+1; h max_length){ bits = max_length; overflow++; } + tree[n*2+1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > max_code) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n-based]; + f = tree[n*2]; + s.opt_len += f * (bits + xbits); + if (stree!=null) s.static_len += f * (stree[n*2+1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do { + bits = max_length-1; + while(s.bl_count[bits]==0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits+1]+=2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) { + n = s.bl_count[bits]; + while (n != 0) { + m = s.heap[--h]; + if (m > max_code) continue; + if (tree[m*2+1] != bits) { + s.opt_len += (int)(((long)bits - (long)tree[m*2+1])*(long)tree[m*2]); + tree[m*2+1] = (short)bits; + } + n--; + } + } + } + + // Construct one Huffman tree and assigns the code bit strings and lengths. + // Update the total bit length for the current block. + // IN assertion: the field freq is set for all tree elements. + // OUT assertions: the fields len and code are set to the optimal bit length + // and corresponding code. The length opt_len is updated; static_len is + // also updated if stree is not null. The field max_code is set. + internal void build_tree(Deflate s){ + short[] tree=dyn_tree; + short[] stree=stat_desc.static_tree; + int elems=stat_desc.elems; + int n, m; // iterate over heap elements + int max_code=-1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for(n=0; n=1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node=elems; // next internal node of the tree + do{ + // n = node of least frequency + n=s.heap[1]; + s.heap[1]=s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m=s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node*2] = (short)(tree[n*2] + tree[m*2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n],s.depth[m])+1); + tree[n*2+1] = tree[m*2+1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while(s.heap_len>=2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + gen_bitlen(s); + + // The field len is now set, we can generate the bit codes + gen_codes(tree, max_code, s.bl_count); + } + + // Generate the codes for a given tree and bit counts (which need not be + // optimal). + // IN assertion: the array bl_count contains the bit length statistics for + // the given tree and the field len is set for all tree elements. + // OUT assertion: the field code is set for all tree elements of non + // zero code length. + internal static void gen_codes(short[] tree, // the tree to decorate + int max_code, // largest code with non zero frequency + short[] bl_count // number of codes at each bit length + ){ + short[] next_code=new short[MAX_BITS+1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (short)((code + bl_count[bits-1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1<>=1; + res<<=1; + } + while(--len>0); + return res>>1; + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/util/zlib/ZDeflaterOutputStream.cs b/src/core/srcbc/util/zlib/ZDeflaterOutputStream.cs new file mode 100644 index 0000000..687453b --- /dev/null +++ b/src/core/srcbc/util/zlib/ZDeflaterOutputStream.cs @@ -0,0 +1,150 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib { + /// + /// Summary description for DeflaterOutputStream. + /// + public class ZDeflaterOutputStream : Stream { + protected ZStream z=new ZStream(); + protected int flushLevel=JZlib.Z_NO_FLUSH; + private const int BUFSIZE = 4192; + protected byte[] buf=new byte[BUFSIZE]; + private byte[] buf1=new byte[1]; + + protected Stream outp; + + public ZDeflaterOutputStream(Stream outp) : this(outp, 6, false) { + } + + public ZDeflaterOutputStream(Stream outp, int level) : this(outp, level, false) { + } + + public ZDeflaterOutputStream(Stream outp, int level, bool nowrap) { + this.outp=outp; + z.deflateInit(level, nowrap); + } + + + public override bool CanRead { + get { + // TODO: Add DeflaterOutputStream.CanRead getter implementation + return false; + } + } + + public override bool CanSeek { + get { + // TODO: Add DeflaterOutputStream.CanSeek getter implementation + return false; + } + } + + public override bool CanWrite { + get { + // TODO: Add DeflaterOutputStream.CanWrite getter implementation + return true; + } + } + + public override long Length { + get { + // TODO: Add DeflaterOutputStream.Length getter implementation + return 0; + } + } + + public override long Position { + get { + // TODO: Add DeflaterOutputStream.Position getter implementation + return 0; + } + set { + // TODO: Add DeflaterOutputStream.Position setter implementation + } + } + + public override void Write(byte[] b, int off, int len) { + if(len==0) + return; + int err; + z.next_in=b; + z.next_in_index=off; + z.avail_in=len; + do{ + z.next_out=buf; + z.next_out_index=0; + z.avail_out=BUFSIZE; + err=z.deflate(flushLevel); + if(err!=JZlib.Z_OK) + throw new IOException("deflating: "+z.msg); + if (z.avail_out < BUFSIZE) + { + outp.Write(buf, 0, BUFSIZE-z.avail_out); + } + } + while(z.avail_in>0 || z.avail_out==0); + } + + public override long Seek(long offset, SeekOrigin origin) { + // TODO: Add DeflaterOutputStream.Seek implementation + return 0; + } + + public override void SetLength(long value) { + // TODO: Add DeflaterOutputStream.SetLength implementation + + } + + public override int Read(byte[] buffer, int offset, int count) { + // TODO: Add DeflaterOutputStream.Read implementation + return 0; + } + + public override void Flush() { + outp.Flush(); + } + + public override void WriteByte(byte b) { + buf1[0]=(byte)b; + Write(buf1, 0, 1); + } + + public void Finish() { + int err; + do{ + z.next_out=buf; + z.next_out_index=0; + z.avail_out=BUFSIZE; + err=z.deflate(JZlib.Z_FINISH); + if(err!=JZlib.Z_STREAM_END && err != JZlib.Z_OK) + throw new IOException("deflating: "+z.msg); + if(BUFSIZE-z.avail_out>0){ + outp.Write(buf, 0, BUFSIZE-z.avail_out); + } + } + while(z.avail_in>0 || z.avail_out==0); + Flush(); + } + + public void End() { + if(z==null) + return; + z.deflateEnd(); + z.free(); + z=null; + } + + public override void Close() { + try{ + try{Finish();} + catch (IOException) {} + } + finally{ + End(); + outp.Close(); + outp=null; + } + } + } +} diff --git a/src/core/srcbc/util/zlib/ZInflaterInputStream.cs b/src/core/srcbc/util/zlib/ZInflaterInputStream.cs new file mode 100644 index 0000000..e846a20 --- /dev/null +++ b/src/core/srcbc/util/zlib/ZInflaterInputStream.cs @@ -0,0 +1,126 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib { + /// + /// Summary description for DeflaterOutputStream. + /// + public class ZInflaterInputStream : Stream { + protected ZStream z=new ZStream(); + protected int flushLevel=JZlib.Z_NO_FLUSH; + private const int BUFSIZE = 4192; + protected byte[] buf=new byte[BUFSIZE]; + private byte[] buf1=new byte[1]; + + protected Stream inp=null; + private bool nomoreinput=false; + + public ZInflaterInputStream(Stream inp) : this(inp, false) { + } + + public ZInflaterInputStream(Stream inp, bool nowrap) { + this.inp=inp; + z.inflateInit(nowrap); + z.next_in=buf; + z.next_in_index=0; + z.avail_in=0; + } + + public override bool CanRead { + get { + // TODO: Add DeflaterOutputStream.CanRead getter implementation + return true; + } + } + + public override bool CanSeek { + get { + // TODO: Add DeflaterOutputStream.CanSeek getter implementation + return false; + } + } + + public override bool CanWrite { + get { + // TODO: Add DeflaterOutputStream.CanWrite getter implementation + return false; + } + } + + public override long Length { + get { + // TODO: Add DeflaterOutputStream.Length getter implementation + return 0; + } + } + + public override long Position { + get { + // TODO: Add DeflaterOutputStream.Position getter implementation + return 0; + } + set { + // TODO: Add DeflaterOutputStream.Position setter implementation + } + } + + public override void Write(byte[] b, int off, int len) { + } + + public override long Seek(long offset, SeekOrigin origin) { + // TODO: Add DeflaterOutputStream.Seek implementation + return 0; + } + + public override void SetLength(long value) { + // TODO: Add DeflaterOutputStream.SetLength implementation + + } + + public override int Read(byte[] b, int off, int len) { + if(len==0) + return(0); + int err; + z.next_out=b; + z.next_out_index=off; + z.avail_out=len; + do { + if((z.avail_in==0)&&(!nomoreinput)) { // if buffer is empty and more input is avaiable, refill it + z.next_in_index=0; + z.avail_in=inp.Read(buf, 0, BUFSIZE);//(BUFSIZEnext_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending(){ + int len=dstate.pending; + + if(len>avail_out) len=avail_out; + if(len==0) return; + + if(dstate.pending_buf.Length<=dstate.pending_out || + next_out.Length<=next_out_index || + dstate.pending_buf.Length<(dstate.pending_out+len) || + next_out.Length<(next_out_index+len)){ + // System.out.println(dstate.pending_buf.length+", "+dstate.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(dstate.pending_buf, dstate.pending_out, + next_out, next_out_index, len); + + next_out_index+=len; + dstate.pending_out+=len; + total_out+=len; + avail_out-=len; + dstate.pending-=len; + if(dstate.pending==0){ + dstate.pending_out=0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) { + int len=avail_in; + + if(len>size) len=size; + if(len==0) return 0; + + avail_in-=len; + + if(dstate.noheader==0) { + adler=_adler.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public void free(){ + next_in=null; + next_out=null; + msg=null; + _adler=null; + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/x509/AttributeCertificateHolder.cs b/src/core/srcbc/x509/AttributeCertificateHolder.cs new file mode 100644 index 0000000..3572848 --- /dev/null +++ b/src/core/srcbc/x509/AttributeCertificateHolder.cs @@ -0,0 +1,420 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.X509 +{ + /// + /// The Holder object. + ///
    + 	/// Holder ::= SEQUENCE {
    + 	///		baseCertificateID   [0] IssuerSerial OPTIONAL,
    + 	///			-- the issuer and serial number of
    + 	///			-- the holder's Public Key Certificate
    + 	///		entityName          [1] GeneralNames OPTIONAL,
    + 	///			-- the name of the claimant or role
    + 	///		objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
    + 	///			-- used to directly authenticate the holder,
    + 	///			-- for example, an executable
    + 	/// }
    +	/// 
    + ///
    + public class AttributeCertificateHolder + //: CertSelector, Selector + : IX509Selector + { + internal readonly Holder holder; + + internal AttributeCertificateHolder( + Asn1Sequence seq) + { + holder = Holder.GetInstance(seq); + } + + public AttributeCertificateHolder( + X509Name issuerName, + BigInteger serialNumber) + { + holder = new Holder( + new IssuerSerial( + GenerateGeneralNames(issuerName), + new DerInteger(serialNumber))); + } + + public AttributeCertificateHolder( + X509Certificate cert) + { + X509Name name; + try + { + name = PrincipalUtilities.GetIssuerX509Principal(cert); + } + catch (Exception e) + { + throw new CertificateParsingException(e.Message); + } + + holder = new Holder(new IssuerSerial(GenerateGeneralNames(name), new DerInteger(cert.SerialNumber))); + } + + public AttributeCertificateHolder( + X509Name principal) + { + holder = new Holder(GenerateGeneralNames(principal)); + } + + /** + * Constructs a holder for v2 attribute certificates with a hash value for + * some type of object. + *

    + * digestedObjectType can be one of the following: + *

      + *
    • 0 - publicKey - A hash of the public key of the holder must be + * passed.
    • + *
    • 1 - publicKeyCert - A hash of the public key certificate of the + * holder must be passed.
    • + *
    • 2 - otherObjectDigest - A hash of some other object type must be + * passed. otherObjectTypeID must not be empty.
    • + *
    + *

    + *

    This cannot be used if a v1 attribute certificate is used.

    + * + * @param digestedObjectType The digest object type. + * @param digestAlgorithm The algorithm identifier for the hash. + * @param otherObjectTypeID The object type ID if + * digestedObjectType is + * otherObjectDigest. + * @param objectDigest The hash value. + */ + public AttributeCertificateHolder( + int digestedObjectType, + string digestAlgorithm, + string otherObjectTypeID, + byte[] objectDigest) + { + // TODO Allow 'objectDigest' to be null? + + holder = new Holder(new ObjectDigestInfo(digestedObjectType, otherObjectTypeID, + new AlgorithmIdentifier(digestAlgorithm), Arrays.Clone(objectDigest))); + } + + /** + * Returns the digest object type if an object digest info is used. + *

    + *

      + *
    • 0 - publicKey - A hash of the public key of the holder must be + * passed.
    • + *
    • 1 - publicKeyCert - A hash of the public key certificate of the + * holder must be passed.
    • + *
    • 2 - otherObjectDigest - A hash of some other object type must be + * passed. otherObjectTypeID must not be empty.
    • + *
    + *

    + * + * @return The digest object type or -1 if no object digest info is set. + */ + public int DigestedObjectType + { + get + { + ObjectDigestInfo odi = holder.ObjectDigestInfo; + + return odi == null + ? -1 + : odi.DigestedObjectType.Value.IntValue; + } + } + + /** + * Returns the other object type ID if an object digest info is used. + * + * @return The other object type ID or null if no object + * digest info is set. + */ + public string DigestAlgorithm + { + get + { + ObjectDigestInfo odi = holder.ObjectDigestInfo; + + return odi == null + ? null + : odi.DigestAlgorithm.ObjectID.Id; + } + } + + /** + * Returns the hash if an object digest info is used. + * + * @return The hash or null if no object digest info is set. + */ + public byte[] GetObjectDigest() + { + ObjectDigestInfo odi = holder.ObjectDigestInfo; + + return odi == null + ? null + : odi.ObjectDigest.GetBytes(); + } + + /** + * Returns the digest algorithm ID if an object digest info is used. + * + * @return The digest algorithm ID or null if no object + * digest info is set. + */ + public string OtherObjectTypeID + { + get + { + ObjectDigestInfo odi = holder.ObjectDigestInfo; + + return odi == null + ? null + : odi.OtherObjectTypeID.Id; + } + } + + private GeneralNames GenerateGeneralNames( + X509Name principal) + { +// return GeneralNames.GetInstance(new DerSequence(new GeneralName(principal))); + return new GeneralNames(new GeneralName(principal)); + } + + private bool MatchesDN( + X509Name subject, + GeneralNames targets) + { + GeneralName[] names = targets.GetNames(); + + for (int i = 0; i != names.Length; i++) + { + GeneralName gn = names[i]; + + if (gn.TagNo == GeneralName.DirectoryName) + { + try + { + if (X509Name.GetInstance(gn.Name).Equivalent(subject)) + { + return true; + } + } + catch (Exception) + { + } + } + } + + return false; + } + + private object[] GetNames( + GeneralName[] names) + { + ArrayList l = new ArrayList(names.Length); + + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == GeneralName.DirectoryName) + { + l.Add(X509Name.GetInstance(names[i].Name)); + } + } + + return l.ToArray(); + } + + private X509Name[] GetPrincipals( + GeneralNames names) + { + object[] p = this.GetNames(names.GetNames()); + ArrayList l = new ArrayList(p.Length); + + for (int i = 0; i != p.Length; i++) + { + if (p[i] is X509Name) + { + l.Add(p[i]); + } + } + + return (X509Name[]) l.ToArray(typeof(X509Name)); + } + + /** + * Return any principal objects inside the attribute certificate holder entity names field. + * + * @return an array of IPrincipal objects (usually X509Name), null if no entity names field is set. + */ + public X509Name[] GetEntityNames() + { + if (holder.EntityName != null) + { + return GetPrincipals(holder.EntityName); + } + + return null; + } + + /** + * Return the principals associated with the issuer attached to this holder + * + * @return an array of principals, null if no BaseCertificateID is set. + */ + public X509Name[] GetIssuer() + { + if (holder.BaseCertificateID != null) + { + return GetPrincipals(holder.BaseCertificateID.Issuer); + } + + return null; + } + + /** + * Return the serial number associated with the issuer attached to this holder. + * + * @return the certificate serial number, null if no BaseCertificateID is set. + */ + public BigInteger SerialNumber + { + get + { + if (holder.BaseCertificateID != null) + { + return holder.BaseCertificateID.Serial.Value; + } + + return null; + } + } + + public object Clone() + { + return new AttributeCertificateHolder((Asn1Sequence)holder.ToAsn1Object()); + } + + public bool Match( +// Certificate cert) + X509Certificate x509Cert) + { +// if (!(cert is X509Certificate)) +// { +// return false; +// } +// +// X509Certificate x509Cert = (X509Certificate)cert; + + try + { + if (holder.BaseCertificateID != null) + { + return holder.BaseCertificateID.Serial.Value.Equals(x509Cert.SerialNumber) + && MatchesDN(PrincipalUtilities.GetIssuerX509Principal(x509Cert), holder.BaseCertificateID.Issuer); + } + + if (holder.EntityName != null) + { + if (MatchesDN(PrincipalUtilities.GetSubjectX509Principal(x509Cert), holder.EntityName)) + { + return true; + } + } + + if (holder.ObjectDigestInfo != null) + { + IDigest md = null; + try + { + md = DigestUtilities.GetDigest(DigestAlgorithm); + } + catch (Exception) + { + return false; + } + + switch (DigestedObjectType) + { + case ObjectDigestInfo.PublicKey: + { + // TODO: DSA Dss-parms + + //byte[] b = x509Cert.GetPublicKey().getEncoded(); + // TODO Is this the right way to encode? + byte[] b = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo( + x509Cert.GetPublicKey()).GetEncoded(); + md.BlockUpdate(b, 0, b.Length); + break; + } + + case ObjectDigestInfo.PublicKeyCert: + { + byte[] b = x509Cert.GetEncoded(); + md.BlockUpdate(b, 0, b.Length); + break; + } + + // TODO Default handler? + } + + // TODO Shouldn't this be the other way around? + if (!Arrays.AreEqual(DigestUtilities.DoFinal(md), GetObjectDigest())) + { + return false; + } + } + } + catch (CertificateEncodingException) + { + return false; + } + + return false; + } + + public override bool Equals( + object obj) + { + if (obj == this) + { + return true; + } + + if (!(obj is AttributeCertificateHolder)) + { + return false; + } + + AttributeCertificateHolder other = (AttributeCertificateHolder)obj; + + return this.holder.Equals(other.holder); + } + + public override int GetHashCode() + { + return this.holder.GetHashCode(); + } + + public bool Match( + object obj) + { + if (!(obj is X509Certificate)) + { + return false; + } + +// return Match((Certificate)obj); + return Match((X509Certificate)obj); + } + } +} diff --git a/src/core/srcbc/x509/AttributeCertificateIssuer.cs b/src/core/srcbc/x509/AttributeCertificateIssuer.cs new file mode 100644 index 0000000..4c0a076 --- /dev/null +++ b/src/core/srcbc/x509/AttributeCertificateIssuer.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.X509 +{ + /** + * Carrying class for an attribute certificate issuer. + */ + public class AttributeCertificateIssuer + //: CertSelector, Selector + : IX509Selector + { + internal readonly Asn1Encodable form; + + /** + * Set the issuer directly with the ASN.1 structure. + * + * @param issuer The issuer + */ + internal AttributeCertificateIssuer( + AttCertIssuer issuer) + { + form = issuer.Issuer; + } + + public AttributeCertificateIssuer( + X509Name principal) + { +// form = new V2Form(GeneralNames.GetInstance(new DerSequence(new GeneralName(principal)))); + form = new V2Form(new GeneralNames(new GeneralName(principal))); + } + + private object[] GetNames() + { + GeneralNames name; + if (form is V2Form) + { + name = ((V2Form)form).IssuerName; + } + else + { + name = (GeneralNames)form; + } + + GeneralName[] names = name.GetNames(); + + ArrayList l = new ArrayList(names.Length); + + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == GeneralName.DirectoryName) + { + l.Add(X509Name.GetInstance(names[i].Name)); + } + } + + return l.ToArray(); + } + + /// Return any principal objects inside the attribute certificate issuer object. + /// An array of IPrincipal objects (usually X509Principal). + public X509Name[] GetPrincipals() + { + object[] p = this.GetNames(); + ArrayList l = new ArrayList(p.Length); + + for (int i = 0; i != p.Length; i++) + { + if (p[i] is X509Name) + { + l.Add(p[i]); + } + } + + return (X509Name[]) l.ToArray(typeof(X509Name)); + } + + private bool MatchesDN( + X509Name subject, + GeneralNames targets) + { + GeneralName[] names = targets.GetNames(); + + for (int i = 0; i != names.Length; i++) + { + GeneralName gn = names[i]; + + if (gn.TagNo == GeneralName.DirectoryName) + { + try + { + if (X509Name.GetInstance(gn.Name).Equivalent(subject)) + { + return true; + } + } + catch (Exception) + { + } + } + } + + return false; + } + + public object Clone() + { + return new AttributeCertificateIssuer(AttCertIssuer.GetInstance(form)); + } + + public bool Match( +// Certificate cert) + X509Certificate x509Cert) + { +// if (!(cert is X509Certificate)) +// { +// return false; +// } +// +// X509Certificate x509Cert = (X509Certificate)cert; + + if (form is V2Form) + { + V2Form issuer = (V2Form) form; + if (issuer.BaseCertificateID != null) + { + return issuer.BaseCertificateID.Serial.Value.Equals(x509Cert.SerialNumber) + && MatchesDN(x509Cert.IssuerDN, issuer.BaseCertificateID.Issuer); + } + + return MatchesDN(x509Cert.SubjectDN, issuer.IssuerName); + } + + return MatchesDN(x509Cert.SubjectDN, (GeneralNames) form); + } + + public override bool Equals( + object obj) + { + if (obj == this) + { + return true; + } + + if (!(obj is AttributeCertificateIssuer)) + { + return false; + } + + AttributeCertificateIssuer other = (AttributeCertificateIssuer)obj; + + return this.form.Equals(other.form); + } + + public override int GetHashCode() + { + return this.form.GetHashCode(); + } + + public bool Match( + object obj) + { + if (!(obj is X509Certificate)) + { + return false; + } + + //return Match((Certificate)obj); + return Match((X509Certificate)obj); + } + } +} diff --git a/src/core/srcbc/x509/IX509AttributeCertificate.cs b/src/core/srcbc/x509/IX509AttributeCertificate.cs new file mode 100644 index 0000000..58d062b --- /dev/null +++ b/src/core/srcbc/x509/IX509AttributeCertificate.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.X509 +{ + /// Interface for an X.509 Attribute Certificate. + public interface IX509AttributeCertificate + : IX509Extension + { + /// The version number for the certificate. + int Version { get; } + + /// The serial number for the certificate. + BigInteger SerialNumber { get; } + + /// The UTC DateTime before which the certificate is not valid. + DateTime NotBefore { get; } + + /// The UTC DateTime after which the certificate is not valid. + DateTime NotAfter { get; } + + /// The holder of the certificate. + AttributeCertificateHolder Holder { get; } + + /// The issuer details for the certificate. + AttributeCertificateIssuer Issuer { get; } + + /// Return the attributes contained in the attribute block in the certificate. + /// An array of attributes. + X509Attribute[] GetAttributes(); + + /// Return the attributes with the same type as the passed in oid. + /// The object identifier we wish to match. + /// An array of matched attributes, null if there is no match. + X509Attribute[] GetAttributes(string oid); + + bool[] GetIssuerUniqueID(); + + bool IsValidNow { get; } + bool IsValid(DateTime date); + + void CheckValidity(); + void CheckValidity(DateTime date); + + byte[] GetSignature(); + + void Verify(AsymmetricKeyParameter publicKey); + + /// Return an ASN.1 encoded byte array representing the attribute certificate. + /// An ASN.1 encoded byte array. + /// If the certificate cannot be encoded. + byte[] GetEncoded(); + } +} diff --git a/src/core/srcbc/x509/IX509Extension.cs b/src/core/srcbc/x509/IX509Extension.cs new file mode 100644 index 0000000..62ed346 --- /dev/null +++ b/src/core/srcbc/x509/IX509Extension.cs @@ -0,0 +1,27 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + public interface IX509Extension + { + /// + /// Get all critical extension values, by oid + /// + /// IDictionary with DerObjectIdentifier keys and Asn1OctetString values + ISet GetCriticalExtensionOids(); + + /// + /// Get all non-critical extension values, by oid + /// + /// IDictionary with DerObjectIdentifier keys and Asn1OctetString values + ISet GetNonCriticalExtensionOids(); + + [Obsolete("Use version taking a DerObjectIdentifier instead")] + Asn1OctetString GetExtensionValue(string oid); + + Asn1OctetString GetExtensionValue(DerObjectIdentifier oid); + } +} diff --git a/src/core/srcbc/x509/PEMParser.cs b/src/core/srcbc/x509/PEMParser.cs new file mode 100644 index 0000000..149e14f --- /dev/null +++ b/src/core/srcbc/x509/PEMParser.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.X509 +{ + class PemParser + { + private readonly string _header1; + private readonly string _header2; + private readonly string _footer1; + private readonly string _footer2; + + internal PemParser( + string type) + { + _header1 = "-----BEGIN " + type + "-----"; + _header2 = "-----BEGIN X509 " + type + "-----"; + _footer1 = "-----END " + type + "-----"; + _footer2 = "-----END X509 " + type + "-----"; + } + + private string ReadLine( + Stream inStream) + { + int c; + StringBuilder l = new StringBuilder(); + + do + { + while (((c = inStream.ReadByte()) != '\r') && c != '\n' && (c >= 0)) + { + if (c == '\r') + { + continue; + } + + l.Append((char)c); + } + } + while (c >= 0 && l.Length == 0); + + if (c < 0) + { + return null; + } + + return l.ToString(); + } + + internal Asn1Sequence ReadPemObject( + Stream inStream) + { + string line; + StringBuilder pemBuf = new StringBuilder(); + + while ((line = ReadLine(inStream)) != null) + { + if (line.Equals(_header1) || line.Equals(_header2)) + { + break; + } + } + + while ((line = ReadLine(inStream)) != null) + { + if (line.Equals(_footer1) || line.Equals(_footer2)) + { + break; + } + + pemBuf.Append(line); + } + + if (pemBuf.Length != 0) + { + Asn1Object o = Asn1Object.FromByteArray(Base64.Decode(pemBuf.ToString())); + + if (!(o is Asn1Sequence)) + { + throw new IOException("malformed PEM data encountered"); + } + + return (Asn1Sequence) o; + } + + return null; + } + } +} + diff --git a/src/core/srcbc/x509/PrincipalUtil.cs b/src/core/srcbc/x509/PrincipalUtil.cs new file mode 100644 index 0000000..3f8239d --- /dev/null +++ b/src/core/srcbc/x509/PrincipalUtil.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509 +{ + /// + /// A utility class that will extract X509Principal objects from X.509 certificates. + ///

    + /// Use this in preference to trying to recreate a principal from a string, not all + /// DNs are what they should be, so it's best to leave them encoded where they + /// can be.

    + ///
    + public class PrincipalUtilities + { + /// Return the issuer of the given cert as an X509Principal. + public static X509Name GetIssuerX509Principal( + X509Certificate cert) + { + try + { + TbsCertificateStructure tbsCert = TbsCertificateStructure.GetInstance( + Asn1Object.FromByteArray(cert.GetTbsCertificate())); + + return tbsCert.Issuer; + } + catch (Exception e) + { + throw new CertificateEncodingException("Could not extract issuer", e); + } + } + + /// Return the subject of the given cert as an X509Principal. + public static X509Name GetSubjectX509Principal( + X509Certificate cert) + { + try + { + TbsCertificateStructure tbsCert = TbsCertificateStructure.GetInstance( + Asn1Object.FromByteArray(cert.GetTbsCertificate())); + + return tbsCert.Subject; + } + catch (Exception e) + { + throw new CertificateEncodingException("Could not extract subject", e); + } + } + + /// Return the issuer of the given CRL as an X509Principal. + public static X509Name GetIssuerX509Principal( + X509Crl crl) + { + try + { + TbsCertificateList tbsCertList = TbsCertificateList.GetInstance( + Asn1Object.FromByteArray(crl.GetTbsCertList())); + + return tbsCertList.Issuer; + } + catch (Exception e) + { + throw new CrlException("Could not extract issuer", e); + } + } + } +} diff --git a/src/core/srcbc/x509/SubjectPublicKeyInfoFactory.cs b/src/core/srcbc/x509/SubjectPublicKeyInfoFactory.cs new file mode 100644 index 0000000..f5a7c35 --- /dev/null +++ b/src/core/srcbc/x509/SubjectPublicKeyInfoFactory.cs @@ -0,0 +1,178 @@ +using System; +using System.IO; +using System.Collections; +using System.Text; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509 +{ + /// + /// A factory to produce Public Key Info Objects. + /// + public sealed class SubjectPublicKeyInfoFactory + { + private SubjectPublicKeyInfoFactory() + { + } + + /// + /// Create a Subject Public Key Info object for a given public key. + /// + /// One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters + /// A subject public key info object. + /// Throw exception if object provided is not one of the above. + public static SubjectPublicKeyInfo CreateSubjectPublicKeyInfo( + AsymmetricKeyParameter key) + { + if (key == null) + throw new ArgumentNullException("key"); + if (key.IsPrivate) + throw new ArgumentException("Private key passed - public key expected.", "key"); + + if (key is ElGamalPublicKeyParameters) + { + ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)key; + ElGamalParameters kp = _key.Parameters; + + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + new AlgorithmIdentifier( + OiwObjectIdentifiers.ElGamalAlgorithm, + new ElGamalParameter(kp.P, kp.G).ToAsn1Object()), + new DerInteger(_key.Y)); + + return info; + } + + if (key is DsaPublicKeyParameters) + { + DsaPublicKeyParameters _key = (DsaPublicKeyParameters) key; + DsaParameters kp = _key.Parameters; + Asn1Encodable ae = kp == null + ? null + : new DsaParameter(kp.P, kp.Q, kp.G).ToAsn1Object(); + + return new SubjectPublicKeyInfo( + new AlgorithmIdentifier(X9ObjectIdentifiers.IdDsa, ae), + new DerInteger(_key.Y)); + } + + if (key is DHPublicKeyParameters) + { + DHPublicKeyParameters _key = (DHPublicKeyParameters) key; + DHParameters kp = _key.Parameters; + + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + new AlgorithmIdentifier( + X9ObjectIdentifiers.DHPublicNumber, + new DHParameter(kp.P, kp.G, kp.L).ToAsn1Object()), + new DerInteger(_key.Y)); + + return info; + } // End of DH + + if (key is RsaKeyParameters) + { + RsaKeyParameters _key = (RsaKeyParameters) key; + + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + new AlgorithmIdentifier(PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance), + new RsaPublicKeyStructure(_key.Modulus, _key.Exponent).ToAsn1Object()); + + return info; + } // End of RSA. + + if (key is ECPublicKeyParameters) + { + ECPublicKeyParameters _key = (ECPublicKeyParameters) key; + + if (_key.AlgorithmName == "ECGOST3410") + { + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + ECPoint q = _key.Q; + BigInteger bX = q.X.ToBigInteger(); + BigInteger bY = q.Y.ToBigInteger(); + + byte[] encKey = new byte[64]; + ExtractBytes(encKey, 0, bX); + ExtractBytes(encKey, 32, bY); + + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + _key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet); + + AlgorithmIdentifier algID = new AlgorithmIdentifier( + CryptoProObjectIdentifiers.GostR3410x2001, + gostParams.ToAsn1Object()); + + return new SubjectPublicKeyInfo(algID, new DerOctetString(encKey)); + } + else + { + ECDomainParameters kp = _key.Parameters; + + X9ECParameters ecP = new X9ECParameters(kp.Curve, kp.G, kp.N, kp.H, kp.GetSeed()); + X962Parameters x962 = new X962Parameters(ecP); + Asn1OctetString p = (Asn1OctetString)(new X9ECPoint(_key.Q).ToAsn1Object()); + + AlgorithmIdentifier algID = new AlgorithmIdentifier( + X9ObjectIdentifiers.IdECPublicKey, x962.ToAsn1Object()); + + return new SubjectPublicKeyInfo(algID, p.GetOctets()); + } + } // End of EC + + if (key is Gost3410PublicKeyParameters) + { + Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) key; + + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + byte[] keyEnc = _key.Y.ToByteArrayUnsigned(); + byte[] keyBytes = new byte[keyEnc.Length]; + + for (int i = 0; i != keyBytes.Length; i++) + { + keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // must be little endian + } + + Gost3410PublicKeyAlgParameters algParams = new Gost3410PublicKeyAlgParameters( + _key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet); + + AlgorithmIdentifier algID = new AlgorithmIdentifier( + CryptoProObjectIdentifiers.GostR3410x94, + algParams.ToAsn1Object()); + + return new SubjectPublicKeyInfo(algID, new DerOctetString(keyBytes)); + } + + throw new ArgumentException("Class provided no convertible: " + key.GetType().FullName); + } + + private static void ExtractBytes( + byte[] encKey, + int offset, + BigInteger bI) + { + byte[] val = bI.ToByteArray(); + int n = (bI.BitLength + 7) / 8; + + for (int i = 0; i < n; ++i) + { + encKey[offset + i] = val[val.Length - 1 - i]; + } + } + } +} diff --git a/src/core/srcbc/x509/X509AttrCertParser.cs b/src/core/srcbc/x509/X509AttrCertParser.cs new file mode 100644 index 0000000..8608b3f --- /dev/null +++ b/src/core/srcbc/x509/X509AttrCertParser.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.X509 +{ + public class X509AttrCertParser + { + private static readonly PemParser PemAttrCertParser = new PemParser("ATTRIBUTE CERTIFICATE"); + + private Asn1Set sData; + private int sDataObjectCount; + private Stream currentStream; + + private IX509AttributeCertificate ReadDerCertificate( + Asn1InputStream dIn) + { + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + + if (seq.Count > 1 && seq[0] is DerObjectIdentifier) + { + if (seq[0].Equals(PkcsObjectIdentifiers.SignedData)) + { + sData = SignedData.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Certificates; + + return GetCertificate(); + } + } + +// return new X509V2AttributeCertificate(seq.getEncoded()); + return new X509V2AttributeCertificate(AttributeCertificate.GetInstance(seq)); + } + + private IX509AttributeCertificate GetCertificate() + { + if (sData != null) + { + while (sDataObjectCount < sData.Count) + { + object obj = sData[sDataObjectCount++]; + + if (obj is Asn1TaggedObject && ((Asn1TaggedObject)obj).TagNo == 2) + { + //return new X509V2AttributeCertificate( + // Asn1Sequence.GetInstance((Asn1TaggedObject)obj, false).GetEncoded()); + return new X509V2AttributeCertificate( + AttributeCertificate.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject)obj, false))); + } + } + } + + return null; + } + + private IX509AttributeCertificate ReadPemCertificate( + Stream inStream) + { + Asn1Sequence seq = PemAttrCertParser.ReadPemObject(inStream); + + return seq == null + ? null + //: new X509V2AttributeCertificate(seq.getEncoded()); + : new X509V2AttributeCertificate(AttributeCertificate.GetInstance(seq)); + } + + /// + /// Create loading data from byte array. + /// + /// + public IX509AttributeCertificate ReadAttrCert( + byte[] input) + { + return ReadAttrCert(new MemoryStream(input, false)); + } + + /// + /// Create loading data from byte array. + /// + /// + public ICollection ReadAttrCerts( + byte[] input) + { + return ReadAttrCerts(new MemoryStream(input, false)); + } + + /** + * Generates a certificate object and initializes it with the data + * read from the input stream inStream. + */ + public IX509AttributeCertificate ReadAttrCert( + Stream inStream) + { + if (inStream == null) + throw new ArgumentNullException("inStream"); + if (!inStream.CanRead) + throw new ArgumentException("inStream must be read-able", "inStream"); + + if (currentStream == null) + { + currentStream = inStream; + sData = null; + sDataObjectCount = 0; + } + else if (currentStream != inStream) // reset if input stream has changed + { + currentStream = inStream; + sData = null; + sDataObjectCount = 0; + } + + try + { + if (sData != null) + { + if (sDataObjectCount != sData.Count) + { + return GetCertificate(); + } + + sData = null; + sDataObjectCount = 0; + return null; + } + + PushbackStream pis = new PushbackStream(inStream); + int tag = pis.ReadByte(); + + if (tag < 0) + return null; + + pis.Unread(tag); + + if (tag != 0x30) // assume ascii PEM encoded. + { + return ReadPemCertificate(pis); + } + + return ReadDerCertificate(new Asn1InputStream(pis)); + } + catch (Exception e) + { + throw new CertificateException(e.ToString()); + } + } + + /** + * Returns a (possibly empty) collection view of the certificates + * read from the given input stream inStream. + */ + public ICollection ReadAttrCerts( + Stream inStream) + { + IX509AttributeCertificate attrCert; + IList attrCerts = new ArrayList(); + + while ((attrCert = ReadAttrCert(inStream)) != null) + { + attrCerts.Add(attrCert); + } + + return attrCerts; + } + } +} \ No newline at end of file diff --git a/src/core/srcbc/x509/X509Attribute.cs b/src/core/srcbc/x509/X509Attribute.cs new file mode 100644 index 0000000..9c3d243 --- /dev/null +++ b/src/core/srcbc/x509/X509Attribute.cs @@ -0,0 +1,76 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.X509 +{ + /** + * Class for carrying the values in an X.509 Attribute. + */ + public class X509Attribute + : Asn1Encodable + { + private readonly AttributeX509 attr; + + /** + * @param at an object representing an attribute. + */ + internal X509Attribute( + Asn1Encodable at) + { + this.attr = AttributeX509.GetInstance(at); + } + + /** + * Create an X.509 Attribute with the type given by the passed in oid and + * the value represented by an ASN.1 Set containing value. + * + * @param oid type of the attribute + * @param value value object to go into the atribute's value set. + */ + public X509Attribute( + string oid, + Asn1Encodable value) + { + this.attr = new AttributeX509(new DerObjectIdentifier(oid), new DerSet(value)); + } + + /** + * Create an X.59 Attribute with the type given by the passed in oid and the + * value represented by an ASN.1 Set containing the objects in value. + * + * @param oid type of the attribute + * @param value vector of values to go in the attribute's value set. + */ + public X509Attribute( + string oid, + Asn1EncodableVector value) + { + this.attr = new AttributeX509(new DerObjectIdentifier(oid), new DerSet(value)); + } + + public string Oid + { + get { return attr.AttrType.Id; } + } + + public Asn1Encodable[] GetValues() + { + Asn1Set s = attr.AttrValues; + Asn1Encodable[] values = new Asn1Encodable[s.Count]; + + for (int i = 0; i != s.Count; i++) + { + values[i] = (Asn1Encodable)s[i]; + } + + return values; + } + + public override Asn1Object ToAsn1Object() + { + return attr.ToAsn1Object(); + } + } +} diff --git a/src/core/srcbc/x509/X509CertPairParser.cs b/src/core/srcbc/x509/X509CertPairParser.cs new file mode 100644 index 0000000..e257794 --- /dev/null +++ b/src/core/srcbc/x509/X509CertPairParser.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.X509 +{ + public class X509CertPairParser + { + private Stream currentStream; + + private X509CertificatePair ReadDerCrossCertificatePair( + Stream inStream) + { + Asn1InputStream dIn = new Asn1InputStream(inStream);//, ProviderUtil.getReadLimit(in)); + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + CertificatePair pair = CertificatePair.GetInstance(seq); + return new X509CertificatePair(pair); + } + + /// + /// Create loading data from byte array. + /// + /// + public X509CertificatePair ReadCertPair( + byte[] input) + { + return ReadCertPair(new MemoryStream(input, false)); + } + + /// + /// Create loading data from byte array. + /// + /// + public ICollection ReadCertPairs( + byte[] input) + { + return ReadCertPairs(new MemoryStream(input, false)); + } + + public X509CertificatePair ReadCertPair( + Stream inStream) + { + if (inStream == null) + throw new ArgumentNullException("inStream"); + if (!inStream.CanRead) + throw new ArgumentException("inStream must be read-able", "inStream"); + + if (currentStream == null) + { + currentStream = inStream; + } + else if (currentStream != inStream) // reset if input stream has changed + { + currentStream = inStream; + } + + try + { + PushbackStream pis = new PushbackStream(inStream); + int tag = pis.ReadByte(); + + if (tag < 0) + return null; + + pis.Unread(tag); + + return ReadDerCrossCertificatePair(pis); + } + catch (Exception e) + { + throw new CertificateException(e.ToString()); + } + } + + public ICollection ReadCertPairs( + Stream inStream) + { + X509CertificatePair certPair; + IList certPairs = new ArrayList(); + + while ((certPair = ReadCertPair(inStream)) != null) + { + certPairs.Add(certPair); + } + + return certPairs; + } + } +} diff --git a/src/core/srcbc/x509/X509Certificate.cs b/src/core/srcbc/x509/X509Certificate.cs new file mode 100644 index 0000000..1126c40 --- /dev/null +++ b/src/core/srcbc/x509/X509Certificate.cs @@ -0,0 +1,579 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Misc; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509 +{ + /// + /// An Object representing an X509 Certificate. + /// Has static methods for loading Certificates encoded in many forms that return X509Certificate Objects. + /// + public class X509Certificate + : X509ExtensionBase +// , PKCS12BagAttributeCarrier + { + private readonly X509CertificateStructure c; +// private Hashtable pkcs12Attributes = new Hashtable(); +// private ArrayList pkcs12Ordering = new ArrayList(); + private readonly BasicConstraints basicConstraints; + private readonly bool[] keyUsage; + + private bool hashValueSet; + private int hashValue; + + protected X509Certificate() + { + } + + public X509Certificate( + X509CertificateStructure c) + { + this.c = c; + + try + { + Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.19")); + + if (str != null) + { + basicConstraints = BasicConstraints.GetInstance( + X509ExtensionUtilities.FromExtensionValue(str)); + } + } + catch (Exception e) + { + throw new CertificateParsingException("cannot construct BasicConstraints: " + e); + } + + try + { + Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.15")); + + if (str != null) + { + DerBitString bits = DerBitString.GetInstance( + X509ExtensionUtilities.FromExtensionValue(str)); + + byte[] bytes = bits.GetBytes(); + int length = (bytes.Length * 8) - bits.PadBits; + + keyUsage = new bool[(length < 9) ? 9 : length]; + + for (int i = 0; i != length; i++) + { +// keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; + keyUsage[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0; + } + } + else + { + keyUsage = null; + } + } + catch (Exception e) + { + throw new CertificateParsingException("cannot construct KeyUsage: " + e); + } + } + +// internal X509Certificate( +// Asn1Sequence seq) +// { +// this.c = X509CertificateStructure.GetInstance(seq); +// } + +// /// +// /// Load certificate from byte array. +// /// +// /// Byte array containing encoded X509Certificate. +// public X509Certificate( +// byte[] encoded) +// : this((Asn1Sequence) new Asn1InputStream(encoded).ReadObject()) +// { +// } +// +// /// +// /// Load certificate from Stream. +// /// Must be positioned at start of certificate. +// /// +// /// +// public X509Certificate( +// Stream input) +// : this((Asn1Sequence) new Asn1InputStream(input).ReadObject()) +// { +// } + + /// + /// Return true if the current time is within the start and end times nominated on the certificate. + /// + /// true id certificate is valid for the current time. + public virtual bool IsValidNow + { + get { return IsValid(DateTime.UtcNow); } + } + + /// + /// Return true if the nominated time is within the start and end times nominated on the certificate. + /// + /// The time to test validity against. + /// True if certificate is valid for nominated time. + public virtual bool IsValid( + DateTime time) + { + return time.CompareTo(NotBefore) >= 0 && time.CompareTo(NotAfter) <= 0; + } + + /// + /// Checks if the current date is within certificate's validity period. + /// + public virtual void CheckValidity() + { + this.CheckValidity(DateTime.UtcNow); + } + + /// + /// Checks if the given date is within certificate's validity period. + /// + /// if the certificate is expired by given date + /// if the certificate is not yet valid on given date + public virtual void CheckValidity( + DateTime time) + { + if (time.CompareTo(NotAfter) > 0) + throw new CertificateExpiredException("certificate expired on " + c.EndDate.GetTime()); + if (time.CompareTo(NotBefore) < 0) + throw new CertificateNotYetValidException("certificate not valid until " + c.StartDate.GetTime()); + } + + /// + /// Return the certificate's version. + /// + /// An integer whose value Equals the version of the cerficate. + public virtual int Version + { + get { return c.Version; } + } + + /// + /// Return a BigInteger containing the serial number. + /// + /// The Serial number. + public virtual BigInteger SerialNumber + { + get { return c.SerialNumber.Value; } + } + + /// + /// Get the Issuer Distinguished Name. (Who signed the certificate.) + /// + /// And X509Object containing name and value pairs. +// public IPrincipal IssuerDN + public virtual X509Name IssuerDN + { + get { return c.Issuer; } + } + + /// + /// Get the subject of this certificate. + /// + /// An X509Name object containing name and value pairs. +// public IPrincipal SubjectDN + public virtual X509Name SubjectDN + { + get { return c.Subject; } + } + + /// + /// The time that this certificate is valid from. + /// + /// A DateTime object representing that time in the local time zone. + public virtual DateTime NotBefore + { + get { return c.StartDate.ToDateTime(); } + } + + /// + /// The time that this certificate is valid up to. + /// + /// A DateTime object representing that time in the local time zone. + public virtual DateTime NotAfter + { + get { return c.EndDate.ToDateTime(); } + } + + /// + /// Return the Der encoded TbsCertificate data. + /// This is the certificate component less the signature. + /// To Get the whole certificate call the GetEncoded() member. + /// + /// A byte array containing the Der encoded Certificate component. + public virtual byte[] GetTbsCertificate() + { + return c.TbsCertificate.GetDerEncoded(); + } + + /// + /// The signature. + /// + /// A byte array containg the signature of the certificate. + public virtual byte[] GetSignature() + { + return c.Signature.GetBytes(); + } + + /// + /// A meaningful version of the Signature Algorithm. (EG SHA1WITHRSA) + /// + /// A sting representing the signature algorithm. + public virtual string SigAlgName + { + get { return SignerUtilities.GetEncodingName(c.SignatureAlgorithm.ObjectID); } + } + + /// + /// Get the Signature Algorithms Object ID. + /// + /// A string containg a '.' separated object id. + public virtual string SigAlgOid + { + get { return c.SignatureAlgorithm.ObjectID.Id; } + } + + /// + /// Get the signature algorithms parameters. (EG DSA Parameters) + /// + /// A byte array containing the Der encoded version of the parameters or null if there are none. + public virtual byte[] GetSigAlgParams() + { + if (c.SignatureAlgorithm.Parameters != null) + { + return c.SignatureAlgorithm.Parameters.GetDerEncoded(); + } + + return null; + } + + /// + /// Get the issuers UID. + /// + /// A DerBitString. + public virtual DerBitString IssuerUniqueID + { + get { return c.TbsCertificate.IssuerUniqueID; } + } + + /// + /// Get the subjects UID. + /// + /// A DerBitString. + public virtual DerBitString SubjectUniqueID + { + get { return c.TbsCertificate.SubjectUniqueID; } + } + + /// + /// Get a key usage guidlines. + /// + public virtual bool[] GetKeyUsage() + { + return keyUsage == null ? null : (bool[]) keyUsage.Clone(); + } + + // TODO Replace with something that returns a list of DerObjectIdentifier + public virtual IList GetExtendedKeyUsage() + { + Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.37")); + + if (str == null) + return null; + + try + { + Asn1Sequence seq = Asn1Sequence.GetInstance( + X509ExtensionUtilities.FromExtensionValue(str)); + + ArrayList list = new ArrayList(); + + foreach (DerObjectIdentifier oid in seq) + { + list.Add(oid.Id); + } + + return list; + } + catch (Exception e) + { + throw new CertificateParsingException("error processing extended key usage extension", e); + } + } + + public virtual int GetBasicConstraints() + { + if (basicConstraints != null && basicConstraints.IsCA()) + { + if (basicConstraints.PathLenConstraint == null) + { + return int.MaxValue; + } + + return basicConstraints.PathLenConstraint.IntValue; + } + + return -1; + } + + public virtual ICollection GetSubjectAlternativeNames() + { + return GetAlternativeNames("2.5.29.17"); + } + + public virtual ICollection GetIssuerAlternativeNames() + { + return GetAlternativeNames("2.5.29.18"); + } + + protected virtual ICollection GetAlternativeNames( + string oid) + { + Asn1OctetString altNames = GetExtensionValue(new DerObjectIdentifier(oid)); + + if (altNames == null) + return null; + + Asn1Object asn1Object = X509ExtensionUtilities.FromExtensionValue(altNames); + + GeneralNames gns = GeneralNames.GetInstance(asn1Object); + + ArrayList result = new ArrayList(); + foreach (GeneralName gn in gns.GetNames()) + { + ArrayList entry = new ArrayList(); + entry.Add(gn.TagNo); + entry.Add(gn.Name.ToString()); + result.Add(entry); + } + return result; + } + + protected override X509Extensions GetX509Extensions() + { + return c.Version == 3 + ? c.TbsCertificate.Extensions + : null; + } + + /// + /// Get the public key of the subject of the certificate. + /// + /// The public key parameters. + public virtual AsymmetricKeyParameter GetPublicKey() + { + return PublicKeyFactory.CreateKey(c.SubjectPublicKeyInfo); + } + + /// + /// Return a Der encoded version of this certificate. + /// + /// A byte array. + public virtual byte[] GetEncoded() + { + return c.GetDerEncoded(); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + X509Certificate other = obj as X509Certificate; + + if (other == null) + return false; + + return c.Equals(other.c); + + // NB: May prefer this implementation of Equals if more than one certificate implementation in play +// return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded()); + } + + public override int GetHashCode() + { + lock (this) + { + if (!hashValueSet) + { + hashValue = c.GetHashCode(); + hashValueSet = true; + } + } + + return hashValue; + } + +// public void setBagAttribute( +// DERObjectIdentifier oid, +// DEREncodable attribute) +// { +// pkcs12Attributes.put(oid, attribute); +// pkcs12Ordering.addElement(oid); +// } +// +// public DEREncodable getBagAttribute( +// DERObjectIdentifier oid) +// { +// return (DEREncodable)pkcs12Attributes.get(oid); +// } +// +// public Enumeration getBagAttributeKeys() +// { +// return pkcs12Ordering.elements(); +// } + + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string nl = Platform.NewLine; + + buf.Append(" [0] Version: ").Append(this.Version).Append(nl); + buf.Append(" SerialNumber: ").Append(this.SerialNumber).Append(nl); + buf.Append(" IssuerDN: ").Append(this.IssuerDN).Append(nl); + buf.Append(" Start Date: ").Append(this.NotBefore).Append(nl); + buf.Append(" Final Date: ").Append(this.NotAfter).Append(nl); + buf.Append(" SubjectDN: ").Append(this.SubjectDN).Append(nl); + buf.Append(" Public Key: ").Append(this.GetPublicKey()).Append(nl); + buf.Append(" Signature Algorithm: ").Append(this.SigAlgName).Append(nl); + + byte[] sig = this.GetSignature(); + byte[] hex = Hex.Encode(sig, 0, 20); + string ascii = Encoding.ASCII.GetString(hex, 0, hex.Length); + buf.Append(" Signature: ").Append(ascii).Append(nl); + + for (int i = 20; i < sig.Length; i += 20) + { + int len = System.Math.Min(20, sig.Length - i); + hex = Hex.Encode(sig, i, len); + ascii = Encoding.ASCII.GetString(hex, 0, hex.Length); + buf.Append(" ").Append(ascii).Append(nl); + } + + X509Extensions extensions = c.TbsCertificate.Extensions; + + if (extensions != null) + { + IEnumerator e = extensions.ExtensionOids.GetEnumerator(); + + if (e.MoveNext()) + { + buf.Append(" Extensions: \n"); + } + + do + { + DerObjectIdentifier oid = (DerObjectIdentifier)e.Current; + X509Extension ext = extensions.GetExtension(oid); + + if (ext.Value != null) + { + byte[] octs = ext.Value.GetOctets(); + Asn1Object obj = Asn1Object.FromByteArray(octs); + buf.Append(" critical(").Append(ext.IsCritical).Append(") "); + try + { + if (oid.Equals(X509Extensions.BasicConstraints)) + { + buf.Append(BasicConstraints.GetInstance(obj)); + } + else if (oid.Equals(X509Extensions.KeyUsage)) + { + buf.Append(KeyUsage.GetInstance(obj)); + } + else if (oid.Equals(MiscObjectIdentifiers.NetscapeCertType)) + { + buf.Append(new NetscapeCertType((DerBitString) obj)); + } + else if (oid.Equals(MiscObjectIdentifiers.NetscapeRevocationUrl)) + { + buf.Append(new NetscapeRevocationUrl((DerIA5String) obj)); + } + else if (oid.Equals(MiscObjectIdentifiers.VerisignCzagExtension)) + { + buf.Append(new VerisignCzagExtension((DerIA5String) obj)); + } + else + { + buf.Append(oid.Id); + buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj)); + //buf.Append(" value = ").Append("*****").Append(nl); + } + } + catch (Exception) + { + buf.Append(oid.Id); + //buf.Append(" value = ").Append(new string(Hex.encode(ext.getValue().getOctets()))).Append(nl); + buf.Append(" value = ").Append("*****"); + } + } + + buf.Append(nl); + } + while (e.MoveNext()); + } + + return buf.ToString(); + } + + /// + /// Verify the certificate's signature using the nominated public key. + /// + /// An appropriate public key parameter object, RsaPublicKeyParameters, DsaPublicKeyParameters or ECDsaPublicKeyParameters + /// True if the signature is valid. + /// If key submitted is not of the above nominated types. + public virtual void Verify( + AsymmetricKeyParameter key) + { + string sigName = X509SignatureUtilities.GetSignatureName(c.SignatureAlgorithm); + ISigner signature = SignerUtilities.GetSigner(sigName); + + CheckSignature(key, signature); + } + + protected virtual void CheckSignature( + AsymmetricKeyParameter publicKey, + ISigner signature) + { + if (!c.SignatureAlgorithm.Equals(c.TbsCertificate.Signature)) + { + throw new CertificateException("signature algorithm in TBS cert not same as outer cert"); + } + + Asn1Encodable parameters = c.SignatureAlgorithm.Parameters; + + X509SignatureUtilities.SetSignatureParameters(signature, parameters); + + signature.Init(false, publicKey); + + byte[] b = this.GetTbsCertificate(); + signature.BlockUpdate(b, 0, b.Length); + + byte[] sig = this.GetSignature(); + if (!signature.VerifySignature(sig)) + { + throw new InvalidKeyException("Public key presented not for certificate signature"); + } + } + } +} diff --git a/src/core/srcbc/x509/X509CertificatePair.cs b/src/core/srcbc/x509/X509CertificatePair.cs new file mode 100644 index 0000000..cfe8712 --- /dev/null +++ b/src/core/srcbc/x509/X509CertificatePair.cs @@ -0,0 +1,117 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509 +{ + /// + /// This class contains a cross certificate pair. Cross certificates pairs may + /// contain two cross signed certificates from two CAs. A certificate from the + /// other CA to this CA is contained in the forward certificate, the certificate + /// from this CA to the other CA is contained in the reverse certificate. + /// + public class X509CertificatePair + { + private readonly X509Certificate forward; + private readonly X509Certificate reverse; + + /// Constructor + /// Certificate from the other CA to this CA. + /// Certificate from this CA to the other CA. + public X509CertificatePair( + X509Certificate forward, + X509Certificate reverse) + { + this.forward = forward; + this.reverse = reverse; + } + + /// Constructor from a ASN.1 CertificatePair structure. + /// The CertificatePair ASN.1 object. + public X509CertificatePair( + CertificatePair pair) + { + if (pair.Forward != null) + { + this.forward = new X509Certificate(pair.Forward); + } + if (pair.Reverse != null) + { + this.reverse = new X509Certificate(pair.Reverse); + } + } + + public byte[] GetEncoded() + { + try + { + X509CertificateStructure f = null, r = null; + + if (forward != null) + { + f = X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(forward.GetEncoded())); + } + + if (reverse != null) + { + r = X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(reverse.GetEncoded())); + } + + return new CertificatePair(f, r).GetDerEncoded(); + } + catch (Exception e) + { + // TODO +// throw new ExtCertificateEncodingException(e.toString(), e); + throw new CertificateEncodingException(e.Message, e); + } + } + + /// Returns the certificate from the other CA to this CA. + public X509Certificate Forward + { + get { return forward; } + } + + /// Returns the certificate from this CA to the other CA. + public X509Certificate Reverse + { + get { return reverse; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + X509CertificatePair other = obj as X509CertificatePair; + + if (other == null) + return false; + + return Platform.Equals(this.forward, other.forward) + && Platform.Equals(this.reverse, other.reverse); + } + + public override int GetHashCode() + { + int hash = -1; + if (forward != null) + { + hash ^= forward.GetHashCode(); + } + if (reverse != null) + { + hash *= 17; + hash ^= reverse.GetHashCode(); + } + return hash; + } + } +} diff --git a/src/core/srcbc/x509/X509CertificateParser.cs b/src/core/srcbc/x509/X509CertificateParser.cs new file mode 100644 index 0000000..1811713 --- /dev/null +++ b/src/core/srcbc/x509/X509CertificateParser.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.X509 +{ + /** + * class for dealing with X509 certificates. + *

    + * At the moment this will deal with "-----BEGIN CERTIFICATE-----" to "-----END CERTIFICATE-----" + * base 64 encoded certs, as well as the BER binaries of certificates and some classes of PKCS#7 + * objects.

    + */ + public class X509CertificateParser + { + private static readonly PemParser PemCertParser = new PemParser("CERTIFICATE"); + + private Asn1Set sData; + private int sDataObjectCount; + private Stream currentStream; + + private X509Certificate ReadDerCertificate( + Asn1InputStream dIn) + { + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + + if (seq.Count > 1 && seq[0] is DerObjectIdentifier) + { + if (seq[0].Equals(PkcsObjectIdentifiers.SignedData)) + { + sData = SignedData.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Certificates; + + return GetCertificate(); + } + } + + return CreateX509Certificate(X509CertificateStructure.GetInstance(seq)); + } + + private X509Certificate GetCertificate() + { + if (sData != null) + { + while (sDataObjectCount < sData.Count) + { + object obj = sData[sDataObjectCount++]; + + if (obj is Asn1Sequence) + { + return CreateX509Certificate( + X509CertificateStructure.GetInstance(obj)); + } + } + } + + return null; + } + + private X509Certificate ReadPemCertificate( + Stream inStream) + { + Asn1Sequence seq = PemCertParser.ReadPemObject(inStream); + + return seq == null + ? null + : CreateX509Certificate(X509CertificateStructure.GetInstance(seq)); + } + + protected virtual X509Certificate CreateX509Certificate( + X509CertificateStructure c) + { + return new X509Certificate(c); + } + + /// + /// Create loading data from byte array. + /// + /// + public X509Certificate ReadCertificate( + byte[] input) + { + return ReadCertificate(new MemoryStream(input, false)); + } + + /// + /// Create loading data from byte array. + /// + /// + public ICollection ReadCertificates( + byte[] input) + { + return ReadCertificates(new MemoryStream(input, false)); + } + + /** + * Generates a certificate object and initializes it with the data + * read from the input stream inStream. + */ + public X509Certificate ReadCertificate( + Stream inStream) + { + if (inStream == null) + throw new ArgumentNullException("inStream"); + if (!inStream.CanRead) + throw new ArgumentException("inStream must be read-able", "inStream"); + + if (currentStream == null) + { + currentStream = inStream; + sData = null; + sDataObjectCount = 0; + } + else if (currentStream != inStream) // reset if input stream has changed + { + currentStream = inStream; + sData = null; + sDataObjectCount = 0; + } + + try + { + if (sData != null) + { + if (sDataObjectCount != sData.Count) + { + return GetCertificate(); + } + + sData = null; + sDataObjectCount = 0; + return null; + } + + PushbackStream pis = new PushbackStream(inStream); + int tag = pis.ReadByte(); + + if (tag < 0) + return null; + + pis.Unread(tag); + + if (tag != 0x30) // assume ascii PEM encoded. + { + return ReadPemCertificate(pis); + } + + return ReadDerCertificate(new Asn1InputStream(pis)); + } + catch (Exception e) + { + throw new CertificateException(e.ToString()); + } + } + + /** + * Returns a (possibly empty) collection view of the certificates + * read from the given input stream inStream. + */ + public ICollection ReadCertificates( + Stream inStream) + { + X509Certificate cert; + IList certs = new ArrayList(); + + while ((cert = ReadCertificate(inStream)) != null) + { + certs.Add(cert); + } + + return certs; + } + } +} diff --git a/src/core/srcbc/x509/X509Crl.cs b/src/core/srcbc/x509/X509Crl.cs new file mode 100644 index 0000000..75b4587 --- /dev/null +++ b/src/core/srcbc/x509/X509Crl.cs @@ -0,0 +1,409 @@ +using System; +using System.Collections; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509 +{ + /** + * The following extensions are listed in RFC 2459 as relevant to CRLs + * + * Authority Key Identifier + * Issuer Alternative Name + * CRL Number + * Delta CRL Indicator (critical) + * Issuing Distribution Point (critical) + */ + public class X509Crl + : X509ExtensionBase + // TODO Add interface Crl? + { + private readonly CertificateList c; + private readonly string sigAlgName; + private readonly byte[] sigAlgParams; + private readonly bool isIndirect; + + public X509Crl( + CertificateList c) + { + this.c = c; + + try + { + this.sigAlgName = X509SignatureUtilities.GetSignatureName(c.SignatureAlgorithm); + + if (c.SignatureAlgorithm.Parameters != null) + { + this.sigAlgParams = ((Asn1Encodable)c.SignatureAlgorithm.Parameters).GetDerEncoded(); + } + else + { + this.sigAlgParams = null; + } + + this.isIndirect = IsIndirectCrl; + } + catch (Exception e) + { + throw new CrlException("CRL contents invalid: " + e); + } + } + + protected override X509Extensions GetX509Extensions() + { + return Version == 2 + ? c.TbsCertList.Extensions + : null; + } + + public virtual byte[] GetEncoded() + { + try + { + return c.GetDerEncoded(); + } + catch (Exception e) + { + throw new CrlException(e.ToString()); + } + } + + public virtual void Verify( + AsymmetricKeyParameter publicKey) + { + if (!c.SignatureAlgorithm.Equals(c.TbsCertList.Signature)) + { + throw new CrlException("Signature algorithm on CertificateList does not match TbsCertList."); + } + + ISigner sig = SignerUtilities.GetSigner(SigAlgName); + sig.Init(false, publicKey); + + byte[] encoded = this.GetTbsCertList(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + if (!sig.VerifySignature(this.GetSignature())) + { + throw new SignatureException("CRL does not verify with supplied public key."); + } + } + + public virtual int Version + { + get { return c.Version; } + } + + public virtual X509Name IssuerDN + { + get { return c.Issuer; } + } + + public virtual DateTime ThisUpdate + { + get { return c.ThisUpdate.ToDateTime(); } + } + + public virtual DateTimeObject NextUpdate + { + get + { + return c.NextUpdate == null + ? null + : new DateTimeObject(c.NextUpdate.ToDateTime()); + } + } + + private ISet LoadCrlEntries() + { + ISet entrySet = new HashSet(); + IEnumerable certs = c.GetRevokedCertificateEnumeration(); + + X509Name previousCertificateIssuer = IssuerDN; + foreach (CrlEntry entry in certs) + { + X509CrlEntry crlEntry = new X509CrlEntry(entry, isIndirect, previousCertificateIssuer); + entrySet.Add(crlEntry); + previousCertificateIssuer = crlEntry.GetCertificateIssuer(); + } + + return entrySet; + } + + public virtual X509CrlEntry GetRevokedCertificate( + BigInteger serialNumber) + { + IEnumerable certs = c.GetRevokedCertificateEnumeration(); + + X509Name previousCertificateIssuer = IssuerDN; + foreach (CrlEntry entry in certs) + { + X509CrlEntry crlEntry = new X509CrlEntry(entry, isIndirect, previousCertificateIssuer); + + if (serialNumber.Equals(entry.UserCertificate.Value)) + { + return crlEntry; + } + + previousCertificateIssuer = crlEntry.GetCertificateIssuer(); + } + + return null; + } + + public virtual ISet GetRevokedCertificates() + { + ISet entrySet = LoadCrlEntries(); + + if (entrySet.Count > 0) + { + return entrySet; // TODO? Collections.unmodifiableSet(entrySet); + } + + return null; + } + + public virtual byte[] GetTbsCertList() + { + try + { + return c.TbsCertList.GetDerEncoded(); + } + catch (Exception e) + { + throw new CrlException(e.ToString()); + } + } + + public virtual byte[] GetSignature() + { + return c.Signature.GetBytes(); + } + + public virtual string SigAlgName + { + get { return sigAlgName; } + } + + public virtual string SigAlgOid + { + get { return c.SignatureAlgorithm.ObjectID.Id; } + } + + public virtual byte[] GetSigAlgParams() + { + return Arrays.Clone(sigAlgParams); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + X509Crl other = obj as X509Crl; + + if (other == null) + return false; + + return c.Equals(other.c); + + // NB: May prefer this implementation of Equals if more than one certificate implementation in play + //return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded()); + } + + public override int GetHashCode() + { + return c.GetHashCode(); + } + + /** + * Returns a string representation of this CRL. + * + * @return a string representation of this CRL. + */ + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string nl = Platform.NewLine; + + buf.Append(" Version: ").Append(this.Version).Append(nl); + buf.Append(" IssuerDN: ").Append(this.IssuerDN).Append(nl); + buf.Append(" This update: ").Append(this.ThisUpdate).Append(nl); + buf.Append(" Next update: ").Append(this.NextUpdate).Append(nl); + buf.Append(" Signature Algorithm: ").Append(this.SigAlgName).Append(nl); + + byte[] sig = this.GetSignature(); + + buf.Append(" Signature: ").Append( + Encoding.ASCII.GetString(Hex.Encode(sig, 0, 20))).Append(nl); + for (int i = 20; i < sig.Length; i += 20) + { + if (i < sig.Length - 20) + { + buf.Append(" ").Append( + Encoding.ASCII.GetString(Hex.Encode(sig, i, 20))).Append(nl); + } + else + { + buf.Append(" ").Append( + Encoding.ASCII.GetString(Hex.Encode(sig, i, sig.Length - i))).Append(nl); + } + } + + X509Extensions extensions = c.TbsCertList.Extensions; + + if (extensions != null) + { + IEnumerator e = extensions.ExtensionOids.GetEnumerator(); + + if (e.MoveNext()) + { + buf.Append(" Extensions: ").Append(nl); + } + + do + { + DerObjectIdentifier oid = (DerObjectIdentifier) e.Current; + X509Extension ext = extensions.GetExtension(oid); + + if (ext.Value != null) + { + Asn1Object asn1Value = X509ExtensionUtilities.FromExtensionValue(ext.Value); + + buf.Append(" critical(").Append(ext.IsCritical).Append(") "); + try + { + if (oid.Equals(X509Extensions.CrlNumber)) + { + buf.Append(new CrlNumber(DerInteger.GetInstance(asn1Value).PositiveValue)).Append(nl); + } + else if (oid.Equals(X509Extensions.DeltaCrlIndicator)) + { + buf.Append( + "Base CRL: " + + new CrlNumber(DerInteger.GetInstance( + asn1Value).PositiveValue)) + .Append(nl); + } + else if (oid.Equals(X509Extensions.IssuingDistributionPoint)) + { + buf.Append(IssuingDistributionPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl); + } + else if (oid.Equals(X509Extensions.CrlDistributionPoints)) + { + buf.Append(CrlDistPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl); + } + else if (oid.Equals(X509Extensions.FreshestCrl)) + { + buf.Append(CrlDistPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl); + } + else + { + buf.Append(oid.Id); + buf.Append(" value = ").Append( + Asn1Dump.DumpAsString(asn1Value)) + .Append(nl); + } + } + catch (Exception) + { + buf.Append(oid.Id); + buf.Append(" value = ").Append("*****").Append(nl); + } + } + else + { + buf.Append(nl); + } + } + while (e.MoveNext()); + } + + ISet certSet = GetRevokedCertificates(); + if (certSet != null) + { + foreach (X509CrlEntry entry in certSet) + { + buf.Append(entry); + buf.Append(nl); + } + } + + return buf.ToString(); + } + + /** + * Checks whether the given certificate is on this CRL. + * + * @param cert the certificate to check for. + * @return true if the given certificate is on this CRL, + * false otherwise. + */ +// public bool IsRevoked( +// Certificate cert) +// { +// if (!cert.getType().Equals("X.509")) +// { +// throw new RuntimeException("X.509 CRL used with non X.509 Cert"); +// } + public virtual bool IsRevoked( + X509Certificate cert) + { + CrlEntry[] certs = c.GetRevokedCertificates(); + + if (certs != null) + { +// BigInteger serial = ((X509Certificate)cert).SerialNumber; + BigInteger serial = cert.SerialNumber; + + for (int i = 0; i < certs.Length; i++) + { + if (certs[i].UserCertificate.Value.Equals(serial)) + { + return true; + } + } + } + + return false; + } + + protected virtual bool IsIndirectCrl + { + get + { + Asn1OctetString idp = GetExtensionValue(X509Extensions.IssuingDistributionPoint); + bool isIndirect = false; + + try + { + if (idp != null) + { + isIndirect = IssuingDistributionPoint.GetInstance( + X509ExtensionUtilities.FromExtensionValue(idp)).IsIndirectCrl; + } + } + catch (Exception e) + { + // TODO +// throw new ExtCrlException("Exception reading IssuingDistributionPoint", e); + throw new CrlException("Exception reading IssuingDistributionPoint" + e); + } + + return isIndirect; + } + } + } +} diff --git a/src/core/srcbc/x509/X509CrlEntry.cs b/src/core/srcbc/x509/X509CrlEntry.cs new file mode 100644 index 0000000..a76e6e9 --- /dev/null +++ b/src/core/srcbc/x509/X509CrlEntry.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509 +{ + /** + * The following extensions are listed in RFC 2459 as relevant to CRL Entries + * + * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer + * (critical) + */ + public class X509CrlEntry + : X509ExtensionBase + { + private CrlEntry c; + private bool isIndirect; + private X509Name previousCertificateIssuer; + private X509Name certificateIssuer; + + public X509CrlEntry( + CrlEntry c) + { + this.c = c; + this.certificateIssuer = loadCertificateIssuer(); + } + + /** + * Constructor for CRLEntries of indirect CRLs. If isIndirect + * is false {@link #getCertificateIssuer()} will always + * return null, previousCertificateIssuer is + * ignored. If this isIndirect is specified and this CrlEntry + * has no certificate issuer CRL entry extension + * previousCertificateIssuer is returned by + * {@link #getCertificateIssuer()}. + * + * @param c + * TbsCertificateList.CrlEntry object. + * @param isIndirect + * true if the corresponding CRL is a indirect + * CRL. + * @param previousCertificateIssuer + * Certificate issuer of the previous CrlEntry. + */ + public X509CrlEntry( + CrlEntry c, + bool isIndirect, + X509Name previousCertificateIssuer) + { + this.c = c; + this.isIndirect = isIndirect; + this.previousCertificateIssuer = previousCertificateIssuer; + this.certificateIssuer = loadCertificateIssuer(); + } + + private X509Name loadCertificateIssuer() + { + if (!isIndirect) + { + return null; + } + + Asn1OctetString ext = GetExtensionValue(X509Extensions.CertificateIssuer); + if (ext == null) + { + return previousCertificateIssuer; + } + + try + { + GeneralName[] names = GeneralNames.GetInstance( + X509ExtensionUtilities.FromExtensionValue(ext)).GetNames(); + + for (int i = 0; i < names.Length; i++) + { + if (names[i].TagNo == GeneralName.DirectoryName) + { + return X509Name.GetInstance(names[i].Name); + } + } + } + catch (Exception) + { + } + + return null; + } + + public X509Name GetCertificateIssuer() + { + return certificateIssuer; + } + + protected override X509Extensions GetX509Extensions() + { + return c.Extensions; + } + + public byte[] GetEncoded() + { + try + { + return c.GetDerEncoded(); + } + catch (Exception e) + { + throw new CrlException(e.ToString()); + } + } + + public BigInteger SerialNumber + { + get { return c.UserCertificate.Value; } + } + + public DateTime RevocationDate + { + get { return c.RevocationDate.ToDateTime(); } + } + + public bool HasExtensions + { + get { return c.Extensions != null; } + } + + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string nl = Platform.NewLine; + + buf.Append(" userCertificate: ").Append(this.SerialNumber).Append(nl); + buf.Append(" revocationDate: ").Append(this.RevocationDate).Append(nl); + buf.Append(" certificateIssuer: ").Append(this.GetCertificateIssuer()).Append(nl); + + X509Extensions extensions = c.Extensions; + + if (extensions != null) + { + IEnumerator e = extensions.ExtensionOids.GetEnumerator(); + if (e.MoveNext()) + { + buf.Append(" crlEntryExtensions:").Append(nl); + + do + { + DerObjectIdentifier oid = (DerObjectIdentifier)e.Current; + X509Extension ext = extensions.GetExtension(oid); + + if (ext.Value != null) + { + Asn1Object obj = Asn1Object.FromByteArray(ext.Value.GetOctets()); + + buf.Append(" critical(") + .Append(ext.IsCritical) + .Append(") "); + try + { + if (oid.Equals(X509Extensions.ReasonCode)) + { + buf.Append(new CrlReason(DerEnumerated.GetInstance(obj))); + } + else if (oid.Equals(X509Extensions.CertificateIssuer)) + { + buf.Append("Certificate issuer: ").Append( + GeneralNames.GetInstance((Asn1Sequence)obj)); + } + else + { + buf.Append(oid.Id); + buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj)); + } + buf.Append(nl); + } + catch (Exception) + { + buf.Append(oid.Id); + buf.Append(" value = ").Append("*****").Append(nl); + } + } + else + { + buf.Append(nl); + } + } + while (e.MoveNext()); + } + } + + return buf.ToString(); + } + } +} diff --git a/src/core/srcbc/x509/X509CrlParser.cs b/src/core/srcbc/x509/X509CrlParser.cs new file mode 100644 index 0000000..5f7027d --- /dev/null +++ b/src/core/srcbc/x509/X509CrlParser.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.X509 +{ + public class X509CrlParser + { + private static readonly PemParser PemCrlParser = new PemParser("CRL"); + + private readonly bool lazyAsn1; + + private Asn1Set sCrlData; + private int sCrlDataObjectCount; + private Stream currentCrlStream; + + public X509CrlParser() + : this(false) + { + } + + public X509CrlParser( + bool lazyAsn1) + { + this.lazyAsn1 = lazyAsn1; + } + + private X509Crl ReadPemCrl( + Stream inStream) + { + Asn1Sequence seq = PemCrlParser.ReadPemObject(inStream); + + return seq == null + ? null + : CreateX509Crl(CertificateList.GetInstance(seq)); + } + + private X509Crl ReadDerCrl( + Asn1InputStream dIn) + { + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + + if (seq.Count > 1 && seq[0] is DerObjectIdentifier) + { + if (seq[0].Equals(PkcsObjectIdentifiers.SignedData)) + { + sCrlData = SignedData.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Crls; + + return GetCrl(); + } + } + + return CreateX509Crl(CertificateList.GetInstance(seq)); + } + + private X509Crl GetCrl() + { + if (sCrlData == null || sCrlDataObjectCount >= sCrlData.Count) + { + return null; + } + + return CreateX509Crl( + CertificateList.GetInstance( + sCrlData[sCrlDataObjectCount++])); + } + + protected virtual X509Crl CreateX509Crl( + CertificateList c) + { + return new X509Crl(c); + } + + /// + /// Create loading data from byte array. + /// + /// + public X509Crl ReadCrl( + byte[] input) + { + return ReadCrl(new MemoryStream(input, false)); + } + + /// + /// Create loading data from byte array. + /// + /// + public ICollection ReadCrls( + byte[] input) + { + return ReadCrls(new MemoryStream(input, false)); + } + + /** + * Generates a certificate revocation list (CRL) object and initializes + * it with the data read from the input stream inStream. + */ + public X509Crl ReadCrl( + Stream inStream) + { + if (inStream == null) + throw new ArgumentNullException("inStream"); + if (!inStream.CanRead) + throw new ArgumentException("inStream must be read-able", "inStream"); + + if (currentCrlStream == null) + { + currentCrlStream = inStream; + sCrlData = null; + sCrlDataObjectCount = 0; + } + else if (currentCrlStream != inStream) // reset if input stream has changed + { + currentCrlStream = inStream; + sCrlData = null; + sCrlDataObjectCount = 0; + } + + try + { + if (sCrlData != null) + { + if (sCrlDataObjectCount != sCrlData.Count) + { + return GetCrl(); + } + + sCrlData = null; + sCrlDataObjectCount = 0; + return null; + } + + PushbackStream pis = new PushbackStream(inStream); + int tag = pis.ReadByte(); + + if (tag < 0) + return null; + + pis.Unread(tag); + + if (tag != 0x30) // assume ascii PEM encoded. + { + return ReadPemCrl(pis); + } + + Asn1InputStream asn1 = lazyAsn1 + ? new LazyAsn1InputStream(pis) + : new Asn1InputStream(pis); + + return ReadDerCrl(asn1); + } + catch (CrlException e) + { + throw e; + } + catch (Exception e) + { + throw new CrlException(e.ToString()); + } + } + + /** + * Returns a (possibly empty) collection view of the CRLs read from + * the given input stream inStream. + * + * The inStream may contain a sequence of DER-encoded CRLs, or + * a PKCS#7 CRL set. This is a PKCS#7 SignedData object, with the + * only significant field being crls. In particular the signature + * and the contents are ignored. + */ + public ICollection ReadCrls( + Stream inStream) + { + X509Crl crl; + IList crls = new ArrayList(); + + while ((crl = ReadCrl(inStream)) != null) + { + crls.Add(crl); + } + + return crls; + } + } +} diff --git a/src/core/srcbc/x509/X509ExtensionBase.cs b/src/core/srcbc/x509/X509ExtensionBase.cs new file mode 100644 index 0000000..05acdb5 --- /dev/null +++ b/src/core/srcbc/x509/X509ExtensionBase.cs @@ -0,0 +1,82 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + public abstract class X509ExtensionBase + : IX509Extension + { + protected abstract X509Extensions GetX509Extensions(); + + protected virtual ISet GetExtensionOids( + bool critical) + { + X509Extensions extensions = GetX509Extensions(); + if (extensions != null) + { + HashSet set = new HashSet(); + foreach (DerObjectIdentifier oid in extensions.ExtensionOids) + { + X509Extension ext = extensions.GetExtension(oid); + if (ext.IsCritical == critical) + { + set.Add(oid.Id); + } + } + + return set; + } + + return null; + } + + /// + /// Get non critical extensions. + /// + /// A set of non critical extension oids. + public virtual ISet GetNonCriticalExtensionOids() + { + return GetExtensionOids(false); + } + + /// + /// Get any critical extensions. + /// + /// A sorted list of critical entension. + public virtual ISet GetCriticalExtensionOids() + { + return GetExtensionOids(true); + } + + /// + /// Get the value of a given extension. + /// + /// The object ID of the extension. + /// An Asn1OctetString object if that extension is found or null if not. + [Obsolete("Use version taking a DerObjectIdentifier instead")] + public Asn1OctetString GetExtensionValue( + string oid) + { + return GetExtensionValue(new DerObjectIdentifier(oid)); + } + + public virtual Asn1OctetString GetExtensionValue( + DerObjectIdentifier oid) + { + X509Extensions exts = GetX509Extensions(); + if (exts != null) + { + X509Extension ext = exts.GetExtension(oid); + if (ext != null) + { + return ext.Value; + } + } + + return null; + } + } +} diff --git a/src/core/srcbc/x509/X509KeyUsage.cs b/src/core/srcbc/x509/X509KeyUsage.cs new file mode 100644 index 0000000..9e21c94 --- /dev/null +++ b/src/core/srcbc/x509/X509KeyUsage.cs @@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.X509 +{ + /** + * A holding class for constructing an X509 Key Usage extension. + * + *
    +	 *    id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
    +	 *
    +	 *    KeyUsage ::= BIT STRING {
    +	 *         digitalSignature        (0),
    +	 *         nonRepudiation          (1),
    +	 *         keyEncipherment         (2),
    +	 *         dataEncipherment        (3),
    +	 *         keyAgreement            (4),
    +	 *         keyCertSign             (5),
    +	 *         cRLSign                 (6),
    +	 *         encipherOnly            (7),
    +	 *         decipherOnly            (8) }
    +	 * 
    + */ + public class X509KeyUsage + : Asn1Encodable + { + public const int DigitalSignature = 1 << 7; + public const int NonRepudiation = 1 << 6; + public const int KeyEncipherment = 1 << 5; + public const int DataEncipherment = 1 << 4; + public const int KeyAgreement = 1 << 3; + public const int KeyCertSign = 1 << 2; + public const int CrlSign = 1 << 1; + public const int EncipherOnly = 1 << 0; + public const int DecipherOnly = 1 << 15; + + private readonly int usage; + + /** + * Basic constructor. + * + * @param usage - the bitwise OR of the Key Usage flags giving the + * allowed uses for the key. + * e.g. (X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment) + */ + public X509KeyUsage( + int usage) + { + this.usage = usage; + } + + public override Asn1Object ToAsn1Object() + { + return new KeyUsage(usage); + } + } +} diff --git a/src/core/srcbc/x509/X509SignatureUtil.cs b/src/core/srcbc/x509/X509SignatureUtil.cs new file mode 100644 index 0000000..9929e22 --- /dev/null +++ b/src/core/srcbc/x509/X509SignatureUtil.cs @@ -0,0 +1,128 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.X509 +{ + internal class X509SignatureUtilities + { + private static readonly Asn1Null derNull = DerNull.Instance; + + internal static void SetSignatureParameters( + ISigner signature, + Asn1Encodable parameters) + { + if (parameters != null && !derNull.Equals(parameters)) + { + // TODO Put back in +// AlgorithmParameters sigParams = AlgorithmParameters.GetInstance(signature.getAlgorithm()); +// +// try +// { +// sigParams.Init(parameters.ToAsn1Object().GetDerEncoded()); +// } +// catch (IOException e) +// { +// throw new SignatureException("IOException decoding parameters: " + e.Message); +// } +// +// if (signature.getAlgorithm().EndsWith("MGF1")) +// { +// try +// { +// signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class)); +// } +// catch (GeneralSecurityException e) +// { +// throw new SignatureException("Exception extracting parameters: " + e.Message); +// } +// } + } + } + + internal static string GetSignatureName( + AlgorithmIdentifier sigAlgId) + { + Asn1Encodable parameters = sigAlgId.Parameters; + + if (parameters != null && !derNull.Equals(parameters)) + { + if (sigAlgId.ObjectID.Equals(PkcsObjectIdentifiers.IdRsassaPss)) + { + RsassaPssParameters rsaParams = RsassaPssParameters.GetInstance(parameters); + + return GetDigestAlgName(rsaParams.HashAlgorithm.ObjectID) + "withRSAandMGF1"; + } + if (sigAlgId.ObjectID.Equals(X9ObjectIdentifiers.ECDsaWithSha2)) + { + Asn1Sequence ecDsaParams = Asn1Sequence.GetInstance(parameters); + + return GetDigestAlgName((DerObjectIdentifier)ecDsaParams[0]) + "withECDSA"; + } + } + + return sigAlgId.ObjectID.Id; + } + + /** + * Return the digest algorithm using one of the standard JCA string + * representations rather than the algorithm identifier (if possible). + */ + private static string GetDigestAlgName( + DerObjectIdentifier digestAlgOID) + { + if (PkcsObjectIdentifiers.MD5.Equals(digestAlgOID)) + { + return "MD5"; + } + else if (OiwObjectIdentifiers.IdSha1.Equals(digestAlgOID)) + { + return "SHA1"; + } + else if (NistObjectIdentifiers.IdSha224.Equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NistObjectIdentifiers.IdSha256.Equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NistObjectIdentifiers.IdSha384.Equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NistObjectIdentifiers.IdSha512.Equals(digestAlgOID)) + { + return "SHA512"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD128.Equals(digestAlgOID)) + { + return "RIPEMD128"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD160.Equals(digestAlgOID)) + { + return "RIPEMD160"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD256.Equals(digestAlgOID)) + { + return "RIPEMD256"; + } + else if (CryptoProObjectIdentifiers.GostR3411.Equals(digestAlgOID)) + { + return "GOST3411"; + } + else + { + return digestAlgOID.Id; + } + } + } +} diff --git a/src/core/srcbc/x509/X509Utilities.cs b/src/core/srcbc/x509/X509Utilities.cs new file mode 100644 index 0000000..6b4cd62 --- /dev/null +++ b/src/core/srcbc/x509/X509Utilities.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + internal class X509Utilities + { + private static readonly Hashtable algorithms = new Hashtable(); + private static readonly Hashtable exParams = new Hashtable(); + private static readonly ISet noParams = new HashSet(); + + static X509Utilities() + { + algorithms.Add("MD2WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD2WITHRSA", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD5WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("MD5WITHRSA", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("SHA1WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA1WITHRSA", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA1WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA224WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA256WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA384WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA512WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("SHA1WITHDSA", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("DSAWITHSHA1", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224); + algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256); + algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224); + algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256); + algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384); + algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512); + algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add(NistObjectIdentifiers.DsaWithSha224); + noParams.Add(NistObjectIdentifiers.DsaWithSha256); + + // + // RFC 4491 + // + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // explicit params + // + AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + exParams.Add("SHA1WITHRSAANDMGF1", CreatePssParams(sha1AlgId, 20)); + + AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance); + exParams.Add("SHA224WITHRSAANDMGF1", CreatePssParams(sha224AlgId, 28)); + + AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance); + exParams.Add("SHA256WITHRSAANDMGF1", CreatePssParams(sha256AlgId, 32)); + + AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance); + exParams.Add("SHA384WITHRSAANDMGF1", CreatePssParams(sha384AlgId, 48)); + + AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance); + exParams.Add("SHA512WITHRSAANDMGF1", CreatePssParams(sha512AlgId, 64)); + } + + private static RsassaPssParameters CreatePssParams( + AlgorithmIdentifier hashAlgId, + int saltSize) + { + return new RsassaPssParameters( + hashAlgId, + new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId), + new DerInteger(saltSize), + new DerInteger(1)); + } + + internal static DerObjectIdentifier GetAlgorithmOid( + string algorithmName) + { + algorithmName = algorithmName.ToUpper(CultureInfo.InvariantCulture); + + if (algorithms.ContainsKey(algorithmName)) + { + return (DerObjectIdentifier) algorithms[algorithmName]; + } + + return new DerObjectIdentifier(algorithmName); + } + + internal static AlgorithmIdentifier GetSigAlgID( + DerObjectIdentifier sigOid, + string algorithmName) + { + if (noParams.Contains(sigOid)) + { + return new AlgorithmIdentifier(sigOid); + } + + algorithmName = algorithmName.ToUpper(CultureInfo.InvariantCulture); + + if (exParams.ContainsKey(algorithmName)) + { + return new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]); + } + + return new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + + internal static IEnumerable GetAlgNames() + { + return new EnumerableProxy(algorithms.Keys); + } + + internal static byte[] GetSignatureForObject( + DerObjectIdentifier sigOid, // TODO Redundant now? + string sigName, + AsymmetricKeyParameter privateKey, + SecureRandom random, + Asn1Encodable ae) + { + if (sigOid == null) + throw new ArgumentNullException("sigOid"); + + ISigner sig = SignerUtilities.GetSigner(sigName); + + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + + byte[] encoded = ae.GetDerEncoded(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + return sig.GenerateSignature(); + } + } +} diff --git a/src/core/srcbc/x509/X509V1CertificateGenerator.cs b/src/core/srcbc/x509/X509V1CertificateGenerator.cs new file mode 100644 index 0000000..ed67d49 --- /dev/null +++ b/src/core/srcbc/x509/X509V1CertificateGenerator.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509 +{ + /// + /// Class to Generate X509V1 Certificates. + /// + public class X509V1CertificateGenerator + { + private V1TbsCertificateGenerator tbsGen; + private DerObjectIdentifier sigOID; + private AlgorithmIdentifier sigAlgId; + private string signatureAlgorithm; + + /// + /// Default Constructor. + /// + public X509V1CertificateGenerator() + { + tbsGen = new V1TbsCertificateGenerator(); + } + + /// + /// Reset the generator. + /// + public void Reset() + { + tbsGen = new V1TbsCertificateGenerator(); + } + + /// + /// Set the certificate's serial number. + /// + /// Make serial numbers long, if you have no serial number policy make sure the number is at least 16 bytes of secure random data. + /// You will be surprised how ugly a serial number collision can get. + /// The serial number. + public void SetSerialNumber( + BigInteger serialNumber) + { + if (serialNumber.SignValue <= 0) + { + throw new ArgumentException("serial number must be a positive integer", "serialNumber"); + } + + tbsGen.SetSerialNumber(new DerInteger(serialNumber)); + } + + /// + /// Set the issuer distinguished name. + /// The issuer is the entity whose private key is used to sign the certificate. + /// + /// The issuers DN. + public void SetIssuerDN( + X509Name issuer) + { + tbsGen.SetIssuer(issuer); + } + + /// + /// Set the date that this certificate is to be valid from. + /// + /// + public void SetNotBefore( + DateTime date) + { + tbsGen.SetStartDate(new Time(date)); + } + + /// + /// Set the date after which this certificate will no longer be valid. + /// + /// + public void SetNotAfter( + DateTime date) + { + tbsGen.SetEndDate(new Time(date)); + } + + /// + /// Set the subject distinguished name. + /// The subject describes the entity associated with the public key. + /// + /// + public void SetSubjectDN( + X509Name subject) + { + tbsGen.SetSubject(subject); + } + + /// + /// Set the public key that this certificate identifies. + /// + /// + public void SetPublicKey( + AsymmetricKeyParameter publicKey) + { + try + { + tbsGen.SetSubjectPublicKeyInfo( + SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey)); + } + catch (Exception e) + { + throw new ArgumentException("unable to process key - " + e.ToString()); + } + } + + /// + /// Set the signature algorithm that will be used to sign this certificate. + /// This can be either a name or an OID, names are treated as case insensitive. + /// + /// string representation of the algorithm name + public void SetSignatureAlgorithm( + string signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + + try + { + sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm); + } + catch (Exception) + { + throw new ArgumentException("Unknown signature type requested", "signatureAlgorithm"); + } + + sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm); + + tbsGen.SetSignature(sigAlgId); + } + + /// + /// Generate a new X509Certificate. + /// + /// The private key of the issuer used to sign this certificate. + /// An X509Certificate. + public X509Certificate Generate( + AsymmetricKeyParameter privateKey) + { + return Generate(privateKey, null); + } + + /// + /// Generate a new X509Certificate specifying a SecureRandom instance that you would like to use. + /// + /// The private key of the issuer used to sign this certificate. + /// The Secure Random you want to use. + /// An X509Certificate. + public X509Certificate Generate( + AsymmetricKeyParameter privateKey, + SecureRandom random) + { + TbsCertificateStructure tbsCert = tbsGen.GenerateTbsCertificate(); + byte[] signature; + + try + { + signature = X509Utilities.GetSignatureForObject( + sigOID, signatureAlgorithm, privateKey, random, tbsCert); + } + catch (Exception e) + { + // TODO +// throw new ExtCertificateEncodingException("exception encoding TBS cert", e); + throw new CertificateEncodingException("exception encoding TBS cert", e); + } + + try + { + return GenerateJcaObject(tbsCert, signature); + } + catch (CertificateParsingException e) + { + // TODO + // throw new ExtCertificateEncodingException("exception producing certificate object", e); + throw new CertificateEncodingException("exception producing certificate object", e); + } + } + + private X509Certificate GenerateJcaObject( + TbsCertificateStructure tbsCert, + byte[] signature) + { + return new X509Certificate( + new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature))); + } + + /// + /// Allows enumeration of the signature names supported by the generator. + /// + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} diff --git a/src/core/srcbc/x509/X509V2AttributeCertificate.cs b/src/core/srcbc/x509/X509V2AttributeCertificate.cs new file mode 100644 index 0000000..b6a6430 --- /dev/null +++ b/src/core/srcbc/x509/X509V2AttributeCertificate.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509 +{ + /// An implementation of a version 2 X.509 Attribute Certificate. + public class X509V2AttributeCertificate + : X509ExtensionBase, IX509AttributeCertificate + { + private readonly AttributeCertificate cert; + private readonly DateTime notBefore; + private readonly DateTime notAfter; + + public X509V2AttributeCertificate( + Stream encIn) + : this(new Asn1InputStream(encIn)) + { + } + + public X509V2AttributeCertificate( + byte[] encoded) + : this(new Asn1InputStream(encoded)) + { + } + + internal X509V2AttributeCertificate( + Asn1InputStream ais) + : this(AttributeCertificate.GetInstance(ais.ReadObject())) + { + } + + internal X509V2AttributeCertificate( + AttributeCertificate cert) + { + this.cert = cert; + + try + { + this.notAfter = cert.ACInfo.AttrCertValidityPeriod.NotAfterTime.ToDateTime(); + this.notBefore = cert.ACInfo.AttrCertValidityPeriod.NotBeforeTime.ToDateTime(); + } + catch (Exception e) + { + throw new IOException("invalid data structure in certificate!", e); + } + } + + public virtual int Version + { + get { return cert.ACInfo.Version.Value.IntValue + 1; } + } + + public virtual BigInteger SerialNumber + { + get { return cert.ACInfo.SerialNumber.Value; } + } + + public virtual AttributeCertificateHolder Holder + { + get + { + return new AttributeCertificateHolder((Asn1Sequence)cert.ACInfo.Holder.ToAsn1Object()); + } + } + + public virtual AttributeCertificateIssuer Issuer + { + get + { + return new AttributeCertificateIssuer(cert.ACInfo.Issuer); + } + } + + public virtual DateTime NotBefore + { + get { return notBefore; } + } + + public virtual DateTime NotAfter + { + get { return notAfter; } + } + + public virtual bool[] GetIssuerUniqueID() + { + DerBitString id = cert.ACInfo.IssuerUniqueID; + + if (id != null) + { + byte[] bytes = id.GetBytes(); + bool[] boolId = new bool[bytes.Length * 8 - id.PadBits]; + + for (int i = 0; i != boolId.Length; i++) + { + //boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; + boolId[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0; + } + + return boolId; + } + + return null; + } + + public virtual bool IsValidNow + { + get { return IsValid(DateTime.UtcNow); } + } + + public virtual bool IsValid( + DateTime date) + { + return date.CompareTo(NotBefore) >= 0 && date.CompareTo(NotAfter) <= 0; + } + + public virtual void CheckValidity() + { + this.CheckValidity(DateTime.UtcNow); + } + + public virtual void CheckValidity( + DateTime date) + { + if (date.CompareTo(NotAfter) > 0) + throw new CertificateExpiredException("certificate expired on " + NotAfter); + if (date.CompareTo(NotBefore) < 0) + throw new CertificateNotYetValidException("certificate not valid until " + NotBefore); + } + + public virtual byte[] GetSignature() + { + return cert.SignatureValue.GetBytes(); + } + + public virtual void Verify( + AsymmetricKeyParameter publicKey) + { + if (!cert.SignatureAlgorithm.Equals(cert.ACInfo.Signature)) + { + throw new CertificateException("Signature algorithm in certificate info not same as outer certificate"); + } + + ISigner signature = SignerUtilities.GetSigner(cert.SignatureAlgorithm.ObjectID.Id); + + signature.Init(false, publicKey); + + try + { + byte[] b = cert.ACInfo.GetEncoded(); + signature.BlockUpdate(b, 0, b.Length); + } + catch (IOException e) + { + throw new SignatureException("Exception encoding certificate info object", e); + } + + if (!signature.VerifySignature(this.GetSignature())) + { + throw new InvalidKeyException("Public key presented not for certificate signature"); + } + } + + public virtual byte[] GetEncoded() + { + return cert.GetEncoded(); + } + + protected override X509Extensions GetX509Extensions() + { + return cert.ACInfo.Extensions; + } + + public virtual X509Attribute[] GetAttributes() + { + Asn1Sequence seq = cert.ACInfo.Attributes; + X509Attribute[] attrs = new X509Attribute[seq.Count]; + + for (int i = 0; i != seq.Count; i++) + { + attrs[i] = new X509Attribute((Asn1Encodable)seq[i]); + } + + return attrs; + } + + public virtual X509Attribute[] GetAttributes( + string oid) + { + Asn1Sequence seq = cert.ACInfo.Attributes; + ArrayList list = new ArrayList(); + + for (int i = 0; i != seq.Count; i++) + { + X509Attribute attr = new X509Attribute((Asn1Encodable)seq[i]); + if (attr.Oid.Equals(oid)) + { + list.Add(attr); + } + } + + if (list.Count < 1) + { + return null; + } + + return (X509Attribute[]) list.ToArray(typeof(X509Attribute)); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + X509V2AttributeCertificate other = obj as X509V2AttributeCertificate; + + if (other == null) + return false; + + return cert.Equals(other.cert); + + // NB: May prefer this implementation of Equals if more than one certificate implementation in play + //return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded()); + } + + public override int GetHashCode() + { + return cert.GetHashCode(); + } + } +} diff --git a/src/core/srcbc/x509/X509V2AttributeCertificateGenerator.cs b/src/core/srcbc/x509/X509V2AttributeCertificateGenerator.cs new file mode 100644 index 0000000..43bc840 --- /dev/null +++ b/src/core/srcbc/x509/X509V2AttributeCertificateGenerator.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509 +{ + /// Class to produce an X.509 Version 2 AttributeCertificate. + public class X509V2AttributeCertificateGenerator + { + private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator(); + + private V2AttributeCertificateInfoGenerator acInfoGen; + private DerObjectIdentifier sigOID; + private AlgorithmIdentifier sigAlgId; + private string signatureAlgorithm; + + public X509V2AttributeCertificateGenerator() + { + acInfoGen = new V2AttributeCertificateInfoGenerator(); + } + + /// Reset the generator + public void Reset() + { + acInfoGen = new V2AttributeCertificateInfoGenerator(); + extGenerator.Reset(); + } + + /// Set the Holder of this Attribute Certificate. + public void SetHolder( + AttributeCertificateHolder holder) + { + acInfoGen.SetHolder(holder.holder); + } + + /// Set the issuer. + public void SetIssuer( + AttributeCertificateIssuer issuer) + { + acInfoGen.SetIssuer(AttCertIssuer.GetInstance(issuer.form)); + } + + /// Set the serial number for the certificate. + public void SetSerialNumber( + BigInteger serialNumber) + { + acInfoGen.SetSerialNumber(new DerInteger(serialNumber)); + } + + public void SetNotBefore( + DateTime date) + { + acInfoGen.SetStartDate(new DerGeneralizedTime(date)); + } + + public void SetNotAfter( + DateTime date) + { + acInfoGen.SetEndDate(new DerGeneralizedTime(date)); + } + + /// + /// Set the signature algorithm. This can be either a name or an OID, names + /// are treated as case insensitive. + /// + /// The algorithm name. + public void SetSignatureAlgorithm( + string signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + + try + { + sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm); + } + catch (Exception) + { + throw new ArgumentException("Unknown signature type requested"); + } + + sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm); + + acInfoGen.SetSignature(sigAlgId); + } + + /// Add an attribute. + public void AddAttribute( + X509Attribute attribute) + { + acInfoGen.AddAttribute(AttributeX509.GetInstance(attribute.ToAsn1Object())); + } + + public void SetIssuerUniqueId( + bool[] iui) + { + // TODO convert bool array to bit string + //acInfoGen.SetIssuerUniqueID(iui); + throw Platform.CreateNotImplementedException("SetIssuerUniqueId()"); + } + + /// Add a given extension field for the standard extensions tag. + public void AddExtension( + string oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); + } + + /// + /// Add a given extension field for the standard extensions tag. + /// The value parameter becomes the contents of the octet string associated + /// with the extension. + /// + public void AddExtension( + string oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); + } + + /// + /// Generate an X509 certificate, based on the current issuer and subject. + /// + public IX509AttributeCertificate Generate( + AsymmetricKeyParameter publicKey) + { + return Generate(publicKey, null); + } + + /// + /// Generate an X509 certificate, based on the current issuer and subject, + /// using the supplied source of randomness, if required. + /// + public IX509AttributeCertificate Generate( + AsymmetricKeyParameter publicKey, + SecureRandom random) + { + if (!extGenerator.IsEmpty) + { + acInfoGen.SetExtensions(extGenerator.Generate()); + } + + AttributeCertificateInfo acInfo = acInfoGen.GenerateAttributeCertificateInfo(); + + Asn1EncodableVector v = new Asn1EncodableVector(); + + v.Add(acInfo, sigAlgId); + + try + { + v.Add(new DerBitString(X509Utilities.GetSignatureForObject(sigOID, signatureAlgorithm, publicKey, random, acInfo))); + + return new X509V2AttributeCertificate(AttributeCertificate.GetInstance(new DerSequence(v))); + } + catch (Exception e) + { + // TODO +// throw new ExtCertificateEncodingException("constructed invalid certificate", e); + throw new CertificateEncodingException("constructed invalid certificate", e); + } + } + + /// + /// Allows enumeration of the signature names supported by the generator. + /// + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} diff --git a/src/core/srcbc/x509/X509V2CRLGenerator.cs b/src/core/srcbc/x509/X509V2CRLGenerator.cs new file mode 100644 index 0000000..2d2c2fc --- /dev/null +++ b/src/core/srcbc/x509/X509V2CRLGenerator.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + /** + * class to produce an X.509 Version 2 CRL. + */ + public class X509V2CrlGenerator + { + private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator(); + + private V2TbsCertListGenerator tbsGen; + private DerObjectIdentifier sigOID; + private AlgorithmIdentifier sigAlgId; + private string signatureAlgorithm; + + public X509V2CrlGenerator() + { + tbsGen = new V2TbsCertListGenerator(); + } + + /** + * reset the generator + */ + public void Reset() + { + tbsGen = new V2TbsCertListGenerator(); + extGenerator.Reset(); + } + + /** + * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the + * certificate. + */ + public void SetIssuerDN( + X509Name issuer) + { + tbsGen.SetIssuer(issuer); + } + + public void SetThisUpdate( + DateTime date) + { + tbsGen.SetThisUpdate(new Time(date)); + } + + public void SetNextUpdate( + DateTime date) + { + tbsGen.SetNextUpdate(new Time(date)); + } + + /** + * Reason being as indicated by CrlReason, i.e. CrlReason.KeyCompromise + * or 0 if CrlReason is not to be used + **/ + public void AddCrlEntry( + BigInteger userCertificate, + DateTime revocationDate, + int reason) + { + tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), reason); + } + + /** + * Add a CRL entry with an Invalidity Date extension as well as a CrlReason extension. + * Reason being as indicated by CrlReason, i.e. CrlReason.KeyCompromise + * or 0 if CrlReason is not to be used + **/ + public void AddCrlEntry( + BigInteger userCertificate, + DateTime revocationDate, + int reason, + DateTime invalidityDate) + { + tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), reason, new DerGeneralizedTime(invalidityDate)); + } + + /** + * Add a CRL entry with extensions. + **/ + public void AddCrlEntry( + BigInteger userCertificate, + DateTime revocationDate, + X509Extensions extensions) + { + tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), extensions); + } + + /** + * Add the CRLEntry objects contained in a previous CRL. + * + * @param other the X509Crl to source the other entries from. + */ + public void AddCrl( + X509Crl other) + { + if (other == null) + throw new ArgumentNullException("other"); + + ISet revocations = other.GetRevokedCertificates(); + + if (revocations != null) + { + foreach (X509CrlEntry entry in revocations) + { + try + { + tbsGen.AddCrlEntry( + Asn1Sequence.GetInstance( + Asn1Object.FromByteArray(entry.GetEncoded()))); + } + catch (IOException e) + { + throw new CrlException("exception processing encoding of CRL", e); + } + } + } + } + + /** + * Set the signature algorithm. This can be either a name or an oid, names + * are treated as case insensitive. + * + * @param signatureAlgorithm string representation of the algorithm name. + */ + public void SetSignatureAlgorithm( + string signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + + try + { + sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm); + } + catch (Exception e) + { + throw new ArgumentException("Unknown signature type requested", e); + } + + sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm); + + tbsGen.SetSignature(sigAlgId); + } + + /** + * add a given extension field for the standard extensions tag (tag 0) + */ + public void AddExtension( + string oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); + } + + /** + * add a given extension field for the standard extensions tag (tag 0) + */ + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(oid, critical, extensionValue); + } + + /** + * add a given extension field for the standard extensions tag (tag 0) + */ + public void AddExtension( + string oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, new DerOctetString(extensionValue)); + } + + /** + * add a given extension field for the standard extensions tag (tag 0) + */ + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue)); + } + + /// Generate an X509 CRL, based on the current issuer and subject. + /// The key used for signing. + public X509Crl Generate( + AsymmetricKeyParameter privateKey) + { + return Generate(privateKey, null); + } + + /// Generate an X509 CRL, based on the current issuer and subject. + /// The key used for signing. + /// A user-defined source of randomness. + public X509Crl Generate( + AsymmetricKeyParameter privateKey, + SecureRandom random) + { + TbsCertificateList tbsCrl = GenerateCertList(); + byte[] signature; + + try + { + signature = X509Utilities.GetSignatureForObject( + sigOID, signatureAlgorithm, privateKey, random, tbsCrl); + } + catch (IOException e) + { + // TODO +// throw new ExtCrlException("cannot generate CRL encoding", e); + throw new CrlException("cannot generate CRL encoding", e); + } + + return GenerateJcaObject(tbsCrl, signature); + } + + private TbsCertificateList GenerateCertList() + { + if (!extGenerator.IsEmpty) + { + tbsGen.SetExtensions(extGenerator.Generate()); + } + + return tbsGen.GenerateTbsCertList(); + } + + private X509Crl GenerateJcaObject( + TbsCertificateList tbsCrl, + byte[] signature) + { + return new X509Crl( + CertificateList.GetInstance( + new DerSequence(tbsCrl, sigAlgId, new DerBitString(signature)))); + } + + /// + /// Allows enumeration of the signature names supported by the generator. + /// + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} diff --git a/src/core/srcbc/x509/X509V3CertificateGenerator.cs b/src/core/srcbc/x509/X509V3CertificateGenerator.cs new file mode 100644 index 0000000..443f045 --- /dev/null +++ b/src/core/srcbc/x509/X509V3CertificateGenerator.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509 +{ + /// + /// A class to Generate Version 3 X509Certificates. + /// + public class X509V3CertificateGenerator + { + private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator(); + + private V3TbsCertificateGenerator tbsGen; + private DerObjectIdentifier sigOid; + private AlgorithmIdentifier sigAlgId; + private string signatureAlgorithm; + + public X509V3CertificateGenerator() + { + tbsGen = new V3TbsCertificateGenerator(); + } + + /// + /// Reset the Generator. + /// + public void Reset() + { + tbsGen = new V3TbsCertificateGenerator(); + extGenerator.Reset(); + } + + /// + /// Set the certificate's serial number. + /// + /// Make serial numbers long, if you have no serial number policy make sure the number is at least 16 bytes of secure random data. + /// You will be surprised how ugly a serial number collision can Get. + /// The serial number. + public void SetSerialNumber( + BigInteger serialNumber) + { + if (serialNumber.SignValue <= 0) + { + throw new ArgumentException("serial number must be a positive integer", "serialNumber"); + } + + tbsGen.SetSerialNumber(new DerInteger(serialNumber)); + } + + /// + /// Set the distinguished name of the issuer. + /// The issuer is the entity which is signing the certificate. + /// + /// The issuer's DN. + public void SetIssuerDN( + X509Name issuer) + { + tbsGen.SetIssuer(issuer); + } + + /// + /// Set the date that this certificate is to be valid from. + /// + /// + public void SetNotBefore( + DateTime date) + { + tbsGen.SetStartDate(new Time(date)); + } + + /// + /// Set the date after which this certificate will no longer be valid. + /// + /// + public void SetNotAfter( + DateTime date) + { + tbsGen.SetEndDate(new Time(date)); + } + + /// + /// Set the DN of the entity that this certificate is about. + /// + /// + public void SetSubjectDN( + X509Name subject) + { + tbsGen.SetSubject(subject); + } + + /// + /// Set the public key that this certificate identifies. + /// + /// + public void SetPublicKey( + AsymmetricKeyParameter publicKey) + { + tbsGen.SetSubjectPublicKeyInfo(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey)); + } + + /// + /// Set the signature algorithm that will be used to sign this certificate. + /// + /// + public void SetSignatureAlgorithm( + string signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + + try + { + sigOid = X509Utilities.GetAlgorithmOid(signatureAlgorithm); + } + catch (Exception) + { + throw new ArgumentException("Unknown signature type requested: " + signatureAlgorithm); + } + + sigAlgId = X509Utilities.GetSigAlgID(sigOid, signatureAlgorithm); + + tbsGen.SetSignature(sigAlgId); + } + + /// + /// Add a given extension field for the standard extensions tag (tag 3). + /// + /// string containing a dotted decimal Object Identifier. + /// Is it critical. + /// The value. + public void AddExtension( + string oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); + } + + /// + /// Add an extension to this certificate. + /// + /// Its Object Identifier. + /// Is it critical. + /// The value. + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(oid, critical, extensionValue); + } + + /// + /// Add an extension using a string with a dotted decimal OID. + /// + /// string containing a dotted decimal Object Identifier. + /// Is it critical. + /// byte[] containing the value of this extension. + public void AddExtension( + string oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, new DerOctetString(extensionValue)); + } + + /// + /// Add an extension to this certificate. + /// + /// Its Object Identifier. + /// Is it critical. + /// byte[] containing the value of this extension. + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue)); + } + + /// + /// Add a given extension field for the standard extensions tag (tag 3), + /// copying the extension value from another certificate. + /// + public void CopyAndAddExtension( + string oid, + bool critical, + X509Certificate cert) + { + CopyAndAddExtension(new DerObjectIdentifier(oid), critical, cert); + } + + /** + * add a given extension field for the standard extensions tag (tag 3) + * copying the extension value from another certificate. + * @throws CertificateParsingException if the extension cannot be extracted. + */ + public void CopyAndAddExtension( + DerObjectIdentifier oid, + bool critical, + X509Certificate cert) + { + Asn1OctetString extValue = cert.GetExtensionValue(oid); + + if (extValue == null) + { + throw new CertificateParsingException("extension " + oid + " not present"); + } + + try + { + Asn1Encodable value = X509ExtensionUtilities.FromExtensionValue(extValue); + + this.AddExtension(oid, critical, value); + } + catch (Exception e) + { + throw new CertificateParsingException(e.Message, e); + } + } + + /// + /// Generate an X509Certificate. + /// + /// The private key of the issuer that is signing this certificate. + /// An X509Certificate. + public X509Certificate Generate( + AsymmetricKeyParameter privateKey) + { + return Generate(privateKey, null); + } + + /// + /// Generate an X509Certificate using your own SecureRandom. + /// + /// The private key of the issuer that is signing this certificate. + /// You Secure Random instance. + /// An X509Certificate. + public X509Certificate Generate( + AsymmetricKeyParameter privateKey, + SecureRandom random) + { + TbsCertificateStructure tbsCert = GenerateTbsCert(); + byte[] signature; + + try + { + signature = X509Utilities.GetSignatureForObject( + sigOid, signatureAlgorithm, privateKey, random, tbsCert); + } + catch (Exception e) + { + // TODO +// throw new ExtCertificateEncodingException("exception encoding TBS cert", e); + throw new CertificateEncodingException("exception encoding TBS cert", e); + } + + try + { + return GenerateJcaObject(tbsCert, signature); + } + catch (CertificateParsingException e) + { + // TODO + // throw new ExtCertificateEncodingException("exception producing certificate object", e); + throw new CertificateEncodingException("exception producing certificate object", e); + } + } + + private TbsCertificateStructure GenerateTbsCert() + { + if (!extGenerator.IsEmpty) + { + tbsGen.SetExtensions(extGenerator.Generate()); + } + + return tbsGen.GenerateTbsCertificate(); + } + + private X509Certificate GenerateJcaObject( + TbsCertificateStructure tbsCert, + byte[] signature) + { + return new X509Certificate( + new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature))); + } + + /// + /// Allows enumeration of the signature names supported by the generator. + /// + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} diff --git a/src/core/srcbc/x509/extension/AuthorityKeyIdentifierStructure.cs b/src/core/srcbc/x509/extension/AuthorityKeyIdentifierStructure.cs new file mode 100644 index 0000000..f827517 --- /dev/null +++ b/src/core/srcbc/x509/extension/AuthorityKeyIdentifierStructure.cs @@ -0,0 +1,105 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509.Extension +{ + /// A high level authority key identifier. + public class AuthorityKeyIdentifierStructure + : AuthorityKeyIdentifier + { + /** + * Constructor which will take the byte[] returned from getExtensionValue() + * + * @param encodedValue a DER octet encoded string with the extension structure in it. + * @throws IOException on parsing errors. + */ + // TODO Add a functional constructor from byte[]? + public AuthorityKeyIdentifierStructure( + Asn1OctetString encodedValue) + : base((Asn1Sequence) X509ExtensionUtilities.FromExtensionValue(encodedValue)) + { + } + + private static Asn1Sequence FromCertificate( + X509Certificate certificate) + { + try + { + GeneralName genName = new GeneralName( + PrincipalUtilities.GetIssuerX509Principal(certificate)); + + if (certificate.Version == 3) + { + Asn1OctetString ext = certificate.GetExtensionValue(X509Extensions.SubjectKeyIdentifier); + + if (ext != null) + { + Asn1OctetString str = (Asn1OctetString) X509ExtensionUtilities.FromExtensionValue(ext); + + return (Asn1Sequence) new AuthorityKeyIdentifier( + str.GetOctets(), new GeneralNames(genName), certificate.SerialNumber).ToAsn1Object(); + } + } + + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo( + certificate.GetPublicKey()); + + return (Asn1Sequence) new AuthorityKeyIdentifier( + info, new GeneralNames(genName), certificate.SerialNumber).ToAsn1Object(); + } + catch (Exception e) + { + throw new CertificateParsingException("Exception extracting certificate details", e); + } + } + + private static Asn1Sequence FromKey( + AsymmetricKeyParameter pubKey) + { + try + { +// SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( +// (Asn1Sequence) Asn1Object.FromByteArray( +// pubKey.GetEncoded())); + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey); + + return (Asn1Sequence) new AuthorityKeyIdentifier(info).ToAsn1Object(); + } + catch (Exception e) + { + throw new InvalidKeyException("can't process key: " + e); + } + } + + /** + * Create an AuthorityKeyIdentifier using the passed in certificate's public + * key, issuer and serial number. + * + * @param certificate the certificate providing the information. + * @throws CertificateParsingException if there is a problem processing the certificate + */ + public AuthorityKeyIdentifierStructure( + X509Certificate certificate) + : base(FromCertificate(certificate)) + { + } + + /** + * Create an AuthorityKeyIdentifier using just the hash of the + * public key. + * + * @param pubKey the key to generate the hash from. + * @throws InvalidKeyException if there is a problem using the key. + */ + public AuthorityKeyIdentifierStructure( + AsymmetricKeyParameter pubKey) + : base(FromKey(pubKey)) + { + } + } +} diff --git a/src/core/srcbc/x509/extension/SubjectKeyIdentifierStructure.cs b/src/core/srcbc/x509/extension/SubjectKeyIdentifierStructure.cs new file mode 100644 index 0000000..7ea4089 --- /dev/null +++ b/src/core/srcbc/x509/extension/SubjectKeyIdentifierStructure.cs @@ -0,0 +1,51 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509.Extension +{ + /** + * A high level subject key identifier. + */ + public class SubjectKeyIdentifierStructure + : SubjectKeyIdentifier + { +// private AuthorityKeyIdentifier authKeyID; + + /** + * Constructor which will take the byte[] returned from getExtensionValue() + * + * @param encodedValue a DER octet encoded string with the extension structure in it. + * @throws IOException on parsing errors. + */ + public SubjectKeyIdentifierStructure( + Asn1OctetString encodedValue) + : base((Asn1OctetString) X509ExtensionUtilities.FromExtensionValue(encodedValue)) + { + } + + private static Asn1OctetString FromPublicKey( + AsymmetricKeyParameter pubKey) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey); + + return (Asn1OctetString) new SubjectKeyIdentifier(info).ToAsn1Object(); + } + catch (Exception e) + { + throw new CertificateParsingException("Exception extracting certificate details: " + e.ToString()); + } + } + + public SubjectKeyIdentifierStructure( + AsymmetricKeyParameter pubKey) + : base(FromPublicKey(pubKey)) + { + } + } +} diff --git a/src/core/srcbc/x509/extension/X509ExtensionUtil.cs b/src/core/srcbc/x509/extension/X509ExtensionUtil.cs new file mode 100644 index 0000000..34f0eab --- /dev/null +++ b/src/core/srcbc/x509/extension/X509ExtensionUtil.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509.Extension +{ + public class X509ExtensionUtilities + { + public static Asn1Object FromExtensionValue( + Asn1OctetString extensionValue) + { + return Asn1Object.FromByteArray(extensionValue.GetOctets()); + } + + public static ICollection GetIssuerAlternativeNames( + X509Certificate cert) + { + Asn1OctetString extVal = cert.GetExtensionValue(X509Extensions.IssuerAlternativeName); + + return GetAlternativeName(extVal); + } + + public static ICollection GetSubjectAlternativeNames( + X509Certificate cert) + { + Asn1OctetString extVal = cert.GetExtensionValue(X509Extensions.SubjectAlternativeName); + + return GetAlternativeName(extVal); + } + + private static ICollection GetAlternativeName( + Asn1OctetString extVal) + { + ArrayList temp = new ArrayList(); + + if (extVal != null) + { + try + { + Asn1Sequence seq = DerSequence.GetInstance(FromExtensionValue(extVal)); + + foreach (GeneralName genName in seq) + { + ArrayList list = new ArrayList(); + list.Add(genName.TagNo); + + switch (genName.TagNo) + { + case GeneralName.EdiPartyName: + case GeneralName.X400Address: + case GeneralName.OtherName: + list.Add(genName.Name.ToAsn1Object()); + break; + case GeneralName.DirectoryName: + list.Add(X509Name.GetInstance(genName.Name).ToString()); + break; + case GeneralName.DnsName: + case GeneralName.Rfc822Name: + case GeneralName.UniformResourceIdentifier: + list.Add(((IAsn1String)genName.Name).GetString()); + break; + case GeneralName.RegisteredID: + list.Add(DerObjectIdentifier.GetInstance(genName.Name).Id); + break; + case GeneralName.IPAddress: + list.Add(DerOctetString.GetInstance(genName.Name).GetOctets()); + break; + default: + throw new IOException("Bad tag number: " + genName.TagNo); + } + + temp.Add(list); + } + } + catch (Exception e) + { + throw new CertificateParsingException(e.Message); + } + } + + return temp; + } + } +} diff --git a/src/core/srcbc/x509/store/IX509Selector.cs b/src/core/srcbc/x509/store/IX509Selector.cs new file mode 100644 index 0000000..41e7031 --- /dev/null +++ b/src/core/srcbc/x509/store/IX509Selector.cs @@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + public interface IX509Selector + : ICloneable + { + bool Match(object obj); + } +} diff --git a/src/core/srcbc/x509/store/IX509Store.cs b/src/core/srcbc/x509/store/IX509Store.cs new file mode 100644 index 0000000..eaf9c54 --- /dev/null +++ b/src/core/srcbc/x509/store/IX509Store.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.X509.Store +{ + public interface IX509Store + { +// void Init(IX509StoreParameters parameters); + ICollection GetMatches(IX509Selector selector); + } +} diff --git a/src/core/srcbc/x509/store/IX509StoreParameters.cs b/src/core/srcbc/x509/store/IX509StoreParameters.cs new file mode 100644 index 0000000..ad93734 --- /dev/null +++ b/src/core/srcbc/x509/store/IX509StoreParameters.cs @@ -0,0 +1,8 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + public interface IX509StoreParameters + { + } +} diff --git a/src/core/srcbc/x509/store/NoSuchStoreException.cs b/src/core/srcbc/x509/store/NoSuchStoreException.cs new file mode 100644 index 0000000..2c36f0d --- /dev/null +++ b/src/core/srcbc/x509/store/NoSuchStoreException.cs @@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + public class NoSuchStoreException + : X509StoreException + { + public NoSuchStoreException() + { + } + + public NoSuchStoreException( + string message) + : base(message) + { + } + + public NoSuchStoreException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/src/core/srcbc/x509/store/X509AttrCertStoreSelector.cs b/src/core/srcbc/x509/store/X509AttrCertStoreSelector.cs new file mode 100644 index 0000000..a0f85a4 --- /dev/null +++ b/src/core/srcbc/x509/store/X509AttrCertStoreSelector.cs @@ -0,0 +1,376 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509.Store +{ + /** + * This class is an Selector like implementation to select + * attribute certificates from a given set of criteria. + * + * @see org.bouncycastle.x509.X509AttributeCertificate + * @see org.bouncycastle.x509.X509Store + */ + public class X509AttrCertStoreSelector + : IX509Selector + { + // TODO: name constraints??? + + private IX509AttributeCertificate attributeCert; + private DateTimeObject attributeCertificateValid; + private AttributeCertificateHolder holder; + private AttributeCertificateIssuer issuer; + private BigInteger serialNumber; + private ISet targetNames = new HashSet(); + private ISet targetGroups = new HashSet(); + + public X509AttrCertStoreSelector() + { + } + + private X509AttrCertStoreSelector( + X509AttrCertStoreSelector o) + { + this.attributeCert = o.attributeCert; + this.attributeCertificateValid = o.attributeCertificateValid; + this.holder = o.holder; + this.issuer = o.issuer; + this.serialNumber = o.serialNumber; + this.targetGroups = new HashSet(o.targetGroups); + this.targetNames = new HashSet(o.targetNames); + } + + /// + /// Decides if the given attribute certificate should be selected. + /// + /// The attribute certificate to be checked. + /// true if the object matches this selector. + public bool Match( + object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + IX509AttributeCertificate attrCert = obj as IX509AttributeCertificate; + + if (attrCert == null) + return false; + + if (this.attributeCert != null && !this.attributeCert.Equals(attrCert)) + return false; + + if (serialNumber != null && !attrCert.SerialNumber.Equals(serialNumber)) + return false; + + if (holder != null && !attrCert.Holder.Equals(holder)) + return false; + + if (issuer != null && !attrCert.Issuer.Equals(issuer)) + return false; + + if (attributeCertificateValid != null && !attrCert.IsValid(attributeCertificateValid.Value)) + return false; + + if (targetNames.Count > 0 || targetGroups.Count > 0) + { + Asn1OctetString targetInfoExt = attrCert.GetExtensionValue( + X509Extensions.TargetInformation); + + if (targetInfoExt != null) + { + TargetInformation targetinfo; + try + { + targetinfo = TargetInformation.GetInstance( + X509ExtensionUtilities.FromExtensionValue(targetInfoExt)); + } + catch (Exception) + { + return false; + } + + Targets[] targetss = targetinfo.GetTargetsObjects(); + + if (targetNames.Count > 0) + { + bool found = false; + + for (int i = 0; i < targetss.Length && !found; i++) + { + Target[] targets = targetss[i].GetTargets(); + + for (int j = 0; j < targets.Length; j++) + { + GeneralName targetName = targets[j].TargetName; + + if (targetName != null && targetNames.Contains(targetName)) + { + found = true; + break; + } + } + } + if (!found) + { + return false; + } + } + + if (targetGroups.Count > 0) + { + bool found = false; + + for (int i = 0; i < targetss.Length && !found; i++) + { + Target[] targets = targetss[i].GetTargets(); + + for (int j = 0; j < targets.Length; j++) + { + GeneralName targetGroup = targets[j].TargetGroup; + + if (targetGroup != null && targetGroups.Contains(targetGroup)) + { + found = true; + break; + } + } + } + + if (!found) + { + return false; + } + } + } + } + + return true; + } + + public object Clone() + { + return new X509AttrCertStoreSelector(this); + } + + /// The attribute certificate which must be matched. + /// If null is given, any will do. + public IX509AttributeCertificate AttributeCert + { + get { return attributeCert; } + set { this.attributeCert = value; } + } + + [Obsolete("Use AttributeCertificateValid instead")] + public DateTimeObject AttribueCertificateValid + { + get { return attributeCertificateValid; } + set { this.attributeCertificateValid = value; } + } + + /// The criteria for validity + /// If null is given any will do. + public DateTimeObject AttributeCertificateValid + { + get { return attributeCertificateValid; } + set { this.attributeCertificateValid = value; } + } + + /// The holder. + /// If null is given any will do. + public AttributeCertificateHolder Holder + { + get { return holder; } + set { this.holder = value; } + } + + /// The issuer. + /// If null is given any will do. + public AttributeCertificateIssuer Issuer + { + get { return issuer; } + set { this.issuer = value; } + } + + /// The serial number. + /// If null is given any will do. + public BigInteger SerialNumber + { + get { return serialNumber; } + set { this.serialNumber = value; } + } + + /** + * Adds a target name criterion for the attribute certificate to the target + * information extension criteria. The X509AttributeCertificate + * must contain at least one of the specified target names. + *

    + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + *

    + * + * @param name The name as a GeneralName (not null) + */ + public void AddTargetName( + GeneralName name) + { + targetNames.Add(name); + } + + /** + * Adds a target name criterion for the attribute certificate to the target + * information extension criteria. The X509AttributeCertificate + * must contain at least one of the specified target names. + *

    + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + *

    + * + * @param name a byte array containing the name in ASN.1 DER encoded form of a GeneralName + * @throws IOException if a parsing error occurs. + */ + public void AddTargetName( + byte[] name) + { + AddTargetName(GeneralName.GetInstance(Asn1Object.FromByteArray(name))); + } + + /** + * Adds a collection with target names criteria. If null is + * given any will do. + *

    + * The collection consists of either GeneralName objects or byte[] arrays representing + * DER encoded GeneralName structures. + *

    + * + * @param names A collection of target names. + * @throws IOException if a parsing error occurs. + * @see #AddTargetName(byte[]) + * @see #AddTargetName(GeneralName) + */ + public void SetTargetNames( + IEnumerable names) + { + targetNames = ExtractGeneralNames(names); + } + + /** + * Gets the target names. The collection consists of Lists + * made up of an Integer in the first entry and a DER encoded + * byte array or a String in the second entry. + *

    The returned collection is immutable.

    + * + * @return The collection of target names + * @see #setTargetNames(Collection) + */ + public IEnumerable GetTargetNames() + { + return new EnumerableProxy(targetNames); + } + + /** + * Adds a target group criterion for the attribute certificate to the target + * information extension criteria. The X509AttributeCertificate + * must contain at least one of the specified target groups. + *

    + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + *

    + * + * @param group The group as GeneralName form (not null) + */ + public void AddTargetGroup( + GeneralName group) + { + targetGroups.Add(group); + } + + /** + * Adds a target group criterion for the attribute certificate to the target + * information extension criteria. The X509AttributeCertificate + * must contain at least one of the specified target groups. + *

    + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + *

    + * + * @param name a byte array containing the group in ASN.1 DER encoded form of a GeneralName + * @throws IOException if a parsing error occurs. + */ + public void AddTargetGroup( + byte[] name) + { + AddTargetGroup(GeneralName.GetInstance(Asn1Object.FromByteArray(name))); + } + + /** + * Adds a collection with target groups criteria. If null is + * given any will do. + *

    + * The collection consists of GeneralName objects or byte[] + * representing DER encoded GeneralNames. + *

    + * + * @param names A collection of target groups. + * @throws IOException if a parsing error occurs. + * @see #AddTargetGroup(byte[]) + * @see #AddTargetGroup(GeneralName) + */ + public void SetTargetGroups( + IEnumerable names) + { + targetGroups = ExtractGeneralNames(names); + } + + /** + * Gets the target groups. The collection consists of Lists + * made up of an Integer in the first entry and a DER encoded + * byte array or a String in the second entry. + *

    The returned collection is immutable.

    + * + * @return The collection of target groups. + * @see #setTargetGroups(Collection) + */ + public IEnumerable GetTargetGroups() + { + return new EnumerableProxy(targetGroups); + } + + private ISet ExtractGeneralNames( + IEnumerable names) + { + ISet result = new HashSet(); + + if (names != null) + { + foreach (object o in names) + { + if (o is GeneralName) + { + result.Add(o); + } + else + { + result.Add(GeneralName.GetInstance(Asn1Object.FromByteArray((byte[]) o))); + } + } + } + + return result; + } + } +} diff --git a/src/core/srcbc/x509/store/X509CertPairStoreSelector.cs b/src/core/srcbc/x509/store/X509CertPairStoreSelector.cs new file mode 100644 index 0000000..86f6c5c --- /dev/null +++ b/src/core/srcbc/x509/store/X509CertPairStoreSelector.cs @@ -0,0 +1,92 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + /// + /// This class is an IX509Selector implementation to select + /// certificate pairs, which are e.g. used for cross certificates. The set of + /// criteria is given from two X509CertStoreSelector objects, + /// each of which, if present, must match the respective component of a pair. + /// + public class X509CertPairStoreSelector + : IX509Selector + { + private static X509CertStoreSelector CloneSelector( + X509CertStoreSelector s) + { + return s == null ? null : (X509CertStoreSelector) s.Clone(); + } + + private X509CertificatePair certPair; + private X509CertStoreSelector forwardSelector; + private X509CertStoreSelector reverseSelector; + + public X509CertPairStoreSelector() + { + } + + private X509CertPairStoreSelector( + X509CertPairStoreSelector o) + { + this.certPair = o.CertPair; + this.forwardSelector = o.ForwardSelector; + this.reverseSelector = o.ReverseSelector; + } + + /// The certificate pair which is used for testing on equality. + public X509CertificatePair CertPair + { + get { return certPair; } + set { this.certPair = value; } + } + + /// The certificate selector for the forward part. + public X509CertStoreSelector ForwardSelector + { + get { return CloneSelector(forwardSelector); } + set { this.forwardSelector = CloneSelector(value); } + } + + /// The certificate selector for the reverse part. + public X509CertStoreSelector ReverseSelector + { + get { return CloneSelector(reverseSelector); } + set { this.reverseSelector = CloneSelector(value); } + } + + /// + /// Decides if the given certificate pair should be selected. If + /// obj is not a X509CertificatePair, this method + /// returns false. + /// + /// The X509CertificatePair to be tested. + /// true if the object matches this selector. + public bool Match( + object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + X509CertificatePair pair = obj as X509CertificatePair; + + if (pair == null) + return false; + + if (certPair != null && !certPair.Equals(pair)) + return false; + + if (forwardSelector != null && !forwardSelector.Match(pair.Forward)) + return false; + + if (reverseSelector != null && !reverseSelector.Match(pair.Reverse)) + return false; + + return true; + } + + public object Clone() + { + return new X509CertPairStoreSelector(this); + } + } +} diff --git a/src/core/srcbc/x509/store/X509CertStoreSelector.cs b/src/core/srcbc/x509/store/X509CertStoreSelector.cs new file mode 100644 index 0000000..b9c2ff6 --- /dev/null +++ b/src/core/srcbc/x509/store/X509CertStoreSelector.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509.Store +{ + public class X509CertStoreSelector + : IX509Selector + { + // TODO Missing criteria? + + private byte[] authorityKeyIdentifier; + private int basicConstraints = -1; + private X509Certificate certificate; + private DateTimeObject certificateValid; + private ISet extendedKeyUsage; + private X509Name issuer; + private bool[] keyUsage; + private ISet policy; + private DateTimeObject privateKeyValid; + private BigInteger serialNumber; + private X509Name subject; + private byte[] subjectKeyIdentifier; + private SubjectPublicKeyInfo subjectPublicKey; + private DerObjectIdentifier subjectPublicKeyAlgID; + + public X509CertStoreSelector() + { + } + + public X509CertStoreSelector( + X509CertStoreSelector o) + { + this.authorityKeyIdentifier = o.AuthorityKeyIdentifier; + this.basicConstraints = o.BasicConstraints; + this.certificate = o.Certificate; + this.certificateValid = o.CertificateValid; + this.extendedKeyUsage = o.ExtendedKeyUsage; + this.issuer = o.Issuer; + this.keyUsage = o.KeyUsage; + this.policy = o.Policy; + this.privateKeyValid = o.PrivateKeyValid; + this.serialNumber = o.SerialNumber; + this.subject = o.Subject; + this.subjectKeyIdentifier = o.SubjectKeyIdentifier; + this.subjectPublicKey = o.SubjectPublicKey; + this.subjectPublicKeyAlgID = o.SubjectPublicKeyAlgID; + } + + public virtual object Clone() + { + return new X509CertStoreSelector(this); + } + + public byte[] AuthorityKeyIdentifier + { + get { return Arrays.Clone(authorityKeyIdentifier); } + set { authorityKeyIdentifier = Arrays.Clone(value); } + } + + public int BasicConstraints + { + get { return basicConstraints; } + set + { + if (value < -2) + throw new ArgumentException("value can't be less than -2", "value"); + + basicConstraints = value; + } + } + + public X509Certificate Certificate + { + get { return certificate; } + set { this.certificate = value; } + } + + public DateTimeObject CertificateValid + { + get { return certificateValid; } + set { certificateValid = value; } + } + + public ISet ExtendedKeyUsage + { + get { return CopySet(extendedKeyUsage); } + set { extendedKeyUsage = CopySet(value); } + } + + public X509Name Issuer + { + get { return issuer; } + set { issuer = value; } + } + + [Obsolete("Avoid working with X509Name objects in string form")] + public string IssuerAsString + { + get { return issuer != null ? issuer.ToString() : null; } + } + + public bool[] KeyUsage + { + get { return CopyBoolArray(keyUsage); } + set { keyUsage = CopyBoolArray(value); } + } + + /// + /// An ISet of DerObjectIdentifier objects. + /// + public ISet Policy + { + get { return CopySet(policy); } + set { policy = CopySet(value); } + } + + public DateTimeObject PrivateKeyValid + { + get { return privateKeyValid; } + set { privateKeyValid = value; } + } + + public BigInteger SerialNumber + { + get { return serialNumber; } + set { serialNumber = value; } + } + + public X509Name Subject + { + get { return subject; } + set { subject = value; } + } + + public string SubjectAsString + { + get { return subject != null ? subject.ToString() : null; } + } + + public byte[] SubjectKeyIdentifier + { + get { return Arrays.Clone(subjectKeyIdentifier); } + set { subjectKeyIdentifier = Arrays.Clone(value); } + } + + public SubjectPublicKeyInfo SubjectPublicKey + { + get { return subjectPublicKey; } + set { subjectPublicKey = value; } + } + + public DerObjectIdentifier SubjectPublicKeyAlgID + { + get { return subjectPublicKeyAlgID; } + set { subjectPublicKeyAlgID = value; } + } + + public virtual bool Match( + object obj) + { + X509Certificate c = obj as X509Certificate; + + if (c == null) + return false; + + if (!MatchExtension(authorityKeyIdentifier, c, X509Extensions.AuthorityKeyIdentifier)) + return false; + + if (basicConstraints != -1) + { + int bc = c.GetBasicConstraints(); + + if (basicConstraints == -2) + { + if (bc != -1) + return false; + } + else + { + if (bc < basicConstraints) + return false; + } + } + + if (certificate != null && !certificate.Equals(c)) + return false; + + if (certificateValid != null && !c.IsValid(certificateValid.Value)) + return false; + + if (extendedKeyUsage != null) + { + IList eku = c.GetExtendedKeyUsage(); + + // Note: if no extended key usage set, all key purposes are implicitly allowed + + if (eku != null) + { + foreach (DerObjectIdentifier oid in extendedKeyUsage) + { + if (!eku.Contains(oid.Id)) + return false; + } + } + } + + if (issuer != null && !issuer.Equivalent(c.IssuerDN)) + return false; + + if (keyUsage != null) + { + bool[] ku = c.GetKeyUsage(); + + // Note: if no key usage set, all key purposes are implicitly allowed + + if (ku != null) + { + for (int i = 0; i < 9; ++i) + { + if (keyUsage[i] && !ku[i]) + return false; + } + } + } + + if (policy != null) + { + Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.CertificatePolicies); + if (extVal == null) + return false; + + Asn1Sequence certPolicies = Asn1Sequence.GetInstance( + X509ExtensionUtilities.FromExtensionValue(extVal)); + + if (policy.Count < 1 && certPolicies.Count < 1) + return false; + + bool found = false; + foreach (PolicyInformation pi in certPolicies) + { + if (policy.Contains(pi.PolicyIdentifier)) + { + found = true; + break; + } + } + + if (!found) + return false; + } + + if (privateKeyValid != null) + { + Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.PrivateKeyUsagePeriod); + if (extVal == null) + return false; + + PrivateKeyUsagePeriod pkup = PrivateKeyUsagePeriod.GetInstance( + X509ExtensionUtilities.FromExtensionValue(extVal)); + + DateTime dt = privateKeyValid.Value; + DateTime notAfter = pkup.NotAfter.ToDateTime(); + DateTime notBefore = pkup.NotBefore.ToDateTime(); + + if (dt.CompareTo(notAfter) > 0 || dt.CompareTo(notBefore) < 0) + return false; + } + + if (serialNumber != null && !serialNumber.Equals(c.SerialNumber)) + return false; + + if (subject != null && !subject.Equivalent(c.SubjectDN)) + return false; + + if (!MatchExtension(subjectKeyIdentifier, c, X509Extensions.SubjectKeyIdentifier)) + return false; + + if (subjectPublicKey != null && !subjectPublicKey.Equals(GetSubjectPublicKey(c))) + return false; + + if (subjectPublicKeyAlgID != null + && !subjectPublicKeyAlgID.Equals(GetSubjectPublicKey(c).AlgorithmID)) + return false; + + return true; + } + + internal static bool IssuersMatch( + X509Name a, + X509Name b) + { + return a == null ? b == null : a.Equivalent(b); + } + + private static bool[] CopyBoolArray( + bool[] b) + { + return b == null ? null : (bool[]) b.Clone(); + } + + private static ISet CopySet( + ISet s) + { + return s == null ? null : new HashSet(s); + } + + private static SubjectPublicKeyInfo GetSubjectPublicKey( + X509Certificate c) + { + return SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(c.GetPublicKey()); + } + + private static bool MatchExtension( + byte[] b, + X509Certificate c, + DerObjectIdentifier oid) + { + if (b == null) + return true; + + Asn1OctetString extVal = c.GetExtensionValue(oid); + + if (extVal == null) + return false; + + return extVal != null && Arrays.AreEqual(b, extVal.GetEncoded()); + } + } +} diff --git a/src/core/srcbc/x509/store/X509CollectionStore.cs b/src/core/srcbc/x509/store/X509CollectionStore.cs new file mode 100644 index 0000000..7a93fca --- /dev/null +++ b/src/core/srcbc/x509/store/X509CollectionStore.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.X509.Store +{ + /** + * A simple collection backed store. + */ + internal class X509CollectionStore + : IX509Store + { + private ICollection _local; + + /** + * Basic constructor. + * + * @param collection - initial contents for the store, this is copied. + */ + internal X509CollectionStore( + ICollection collection) + { + _local = new ArrayList(collection); + } + + /** + * Return the matches in the collection for the passed in selector. + * + * @param selector the selector to match against. + * @return a possibly empty collection of matching objects. + */ + public ICollection GetMatches( + IX509Selector selector) + { + if (selector == null) + { + return new ArrayList(_local); + } + + IList result = new ArrayList(); + foreach (object obj in _local) + { + if (selector.Match(obj)) + result.Add(obj); + } + + return result; + } + } +} diff --git a/src/core/srcbc/x509/store/X509CollectionStoreParameters.cs b/src/core/srcbc/x509/store/X509CollectionStoreParameters.cs new file mode 100644 index 0000000..6019618 --- /dev/null +++ b/src/core/srcbc/x509/store/X509CollectionStoreParameters.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using System.Text; + +namespace Org.BouncyCastle.X509.Store +{ + /// This class contains a collection for collection based X509Stores. + public class X509CollectionStoreParameters + : IX509StoreParameters + { + private readonly ArrayList collection; + + /// + /// Constructor. + ///

    + /// The collection is copied. + ///

    + ///
    + /// The collection containing X.509 object types. + /// If collection is null. + public X509CollectionStoreParameters( + ICollection collection) + { + if (collection == null) + throw new ArgumentNullException("collection"); + + this.collection = new ArrayList(collection); + } + + // TODO Do we need to be able to Clone() these, and should it really be shallow? +// /** +// * Returns a shallow clone. The returned contents are not copied, so adding +// * or removing objects will effect this. +// * +// * @return a shallow clone. +// */ +// public object Clone() +// { +// return new X509CollectionStoreParameters(collection); +// } + + /// Returns a copy of the ICollection. + public ICollection GetCollection() + { + return new ArrayList(collection); + } + + /// Returns a formatted string describing the parameters. + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("X509CollectionStoreParameters: [\n"); + sb.Append(" collection: " + collection + "\n"); + sb.Append("]"); + return sb.ToString(); + } + } +} diff --git a/src/core/srcbc/x509/store/X509CrlStoreSelector.cs b/src/core/srcbc/x509/store/X509CrlStoreSelector.cs new file mode 100644 index 0000000..a09229e --- /dev/null +++ b/src/core/srcbc/x509/store/X509CrlStoreSelector.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509.Store +{ + public class X509CrlStoreSelector + : IX509Selector + { + // TODO Missing criteria? + + private X509Certificate certificateChecking; + private DateTimeObject dateAndTime; + private ICollection issuers; + private BigInteger maxCrlNumber; + private BigInteger minCrlNumber; + + private IX509AttributeCertificate attrCertChecking; + private bool completeCrlEnabled; + private bool deltaCrlIndicatorEnabled; + private byte[] issuingDistributionPoint; + private bool issuingDistributionPointEnabled; + private BigInteger maxBaseCrlNumber; + + public X509CrlStoreSelector() + { + } + + public X509CrlStoreSelector( + X509CrlStoreSelector o) + { + this.certificateChecking = o.CertificateChecking; + this.dateAndTime = o.DateAndTime; + this.issuers = o.Issuers; + this.maxCrlNumber = o.MaxCrlNumber; + this.minCrlNumber = o.MinCrlNumber; + + this.deltaCrlIndicatorEnabled = o.DeltaCrlIndicatorEnabled; + this.completeCrlEnabled = o.CompleteCrlEnabled; + this.maxBaseCrlNumber = o.MaxBaseCrlNumber; + this.attrCertChecking = o.AttrCertChecking; + this.issuingDistributionPointEnabled = o.IssuingDistributionPointEnabled; + this.issuingDistributionPoint = o.IssuingDistributionPoint; + } + + public virtual object Clone() + { + return new X509CrlStoreSelector(this); + } + + public X509Certificate CertificateChecking + { + get { return certificateChecking; } + set { certificateChecking = value; } + } + + public DateTimeObject DateAndTime + { + get { return dateAndTime; } + set { dateAndTime = value; } + } + + /// + /// An ICollection of X509Name objects + /// + public ICollection Issuers + { + get { return new ArrayList(issuers); } + set { issuers = new ArrayList(value); } + } + + public BigInteger MaxCrlNumber + { + get { return maxCrlNumber; } + set { maxCrlNumber = value; } + } + + public BigInteger MinCrlNumber + { + get { return minCrlNumber; } + set { minCrlNumber = value; } + } + + /** + * The attribute certificate being checked. This is not a criterion. + * Rather, it is optional information that may help a {@link X509Store} find + * CRLs that would be relevant when checking revocation for the specified + * attribute certificate. If null is specified, then no such + * optional information is provided. + * + * @param attrCert the IX509AttributeCertificate being checked (or + * null) + * @see #getAttrCertificateChecking() + */ + public IX509AttributeCertificate AttrCertChecking + { + get { return attrCertChecking; } + set { this.attrCertChecking = value; } + } + + /** + * If true only complete CRLs are returned. Defaults to + * false. + * + * @return true if only complete CRLs are returned. + */ + public bool CompleteCrlEnabled + { + get { return completeCrlEnabled; } + set { this.completeCrlEnabled = value; } + } + + /** + * Returns if this selector must match CRLs with the delta CRL indicator + * extension set. Defaults to false. + * + * @return Returns true if only CRLs with the delta CRL + * indicator extension are selected. + */ + public bool DeltaCrlIndicatorEnabled + { + get { return deltaCrlIndicatorEnabled; } + set { this.deltaCrlIndicatorEnabled = value; } + } + + /** + * The issuing distribution point. + *

    + * The issuing distribution point extension is a CRL extension which + * identifies the scope and the distribution point of a CRL. The scope + * contains among others information about revocation reasons contained in + * the CRL. Delta CRLs and complete CRLs must have matching issuing + * distribution points.

    + *

    + * The byte array is cloned to protect against subsequent modifications.

    + *

    + * You must also enable or disable this criteria with + * {@link #setIssuingDistributionPointEnabled(bool)}.

    + * + * @param issuingDistributionPoint The issuing distribution point to set. + * This is the DER encoded OCTET STRING extension value. + * @see #getIssuingDistributionPoint() + */ + public byte[] IssuingDistributionPoint + { + get { return Arrays.Clone(issuingDistributionPoint); } + set { this.issuingDistributionPoint = Arrays.Clone(value); } + } + + /** + * Whether the issuing distribution point criteria should be applied. + * Defaults to false. + *

    + * You may also set the issuing distribution point criteria if not a missing + * issuing distribution point should be assumed.

    + * + * @return Returns if the issuing distribution point check is enabled. + */ + public bool IssuingDistributionPointEnabled + { + get { return issuingDistributionPointEnabled; } + set { this.issuingDistributionPointEnabled = value; } + } + + /** + * The maximum base CRL number. Defaults to null. + * + * @return Returns the maximum base CRL number. + * @see #setMaxBaseCRLNumber(BigInteger) + */ + public BigInteger MaxBaseCrlNumber + { + get { return maxBaseCrlNumber; } + set { this.maxBaseCrlNumber = value; } + } + + public virtual bool Match( + object obj) + { + X509Crl c = obj as X509Crl; + + if (c == null) + return false; + + if (dateAndTime != null) + { + DateTime dt = dateAndTime.Value; + DateTime tu = c.ThisUpdate; + DateTimeObject nu = c.NextUpdate; + + if (dt.CompareTo(tu) < 0 || nu == null || dt.CompareTo(nu.Value) >= 0) + return false; + } + + if (issuers != null) + { + X509Name i = c.IssuerDN; + + bool found = false; + + foreach (X509Name issuer in issuers) + { + if (issuer.Equivalent(i)) + { + found = true; + break; + } + } + + if (!found) + return false; + } + + if (maxCrlNumber != null || minCrlNumber != null) + { + Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.CrlNumber); + if (extVal == null) + return false; + + BigInteger cn = CrlNumber.GetInstance( + X509ExtensionUtilities.FromExtensionValue(extVal)).PositiveValue; + + if (maxCrlNumber != null && cn.CompareTo(maxCrlNumber) > 0) + return false; + + if (minCrlNumber != null && cn.CompareTo(minCrlNumber) < 0) + return false; + } + + DerInteger dci = null; + try + { + Asn1OctetString bytes = c.GetExtensionValue(X509Extensions.DeltaCrlIndicator); + if (bytes != null) + { + dci = DerInteger.GetInstance(X509ExtensionUtilities.FromExtensionValue(bytes)); + } + } + catch (Exception) + { + return false; + } + + if (dci == null) + { + if (DeltaCrlIndicatorEnabled) + return false; + } + else + { + if (CompleteCrlEnabled) + return false; + + if (maxBaseCrlNumber != null && dci.PositiveValue.CompareTo(maxBaseCrlNumber) > 0) + return false; + } + + if (issuingDistributionPointEnabled) + { + Asn1OctetString idp = c.GetExtensionValue(X509Extensions.IssuingDistributionPoint); + if (issuingDistributionPoint == null) + { + if (idp != null) + return false; + } + else + { + if (!Arrays.AreEqual(idp.GetOctets(), issuingDistributionPoint)) + return false; + } + } + + return true; + } + } +} diff --git a/src/core/srcbc/x509/store/X509StoreException.cs b/src/core/srcbc/x509/store/X509StoreException.cs new file mode 100644 index 0000000..cd6e47d --- /dev/null +++ b/src/core/srcbc/x509/store/X509StoreException.cs @@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + public class X509StoreException + : Exception + { + public X509StoreException() + { + } + + public X509StoreException( + string message) + : base(message) + { + } + + public X509StoreException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/src/core/srcbc/x509/store/X509StoreFactory.cs b/src/core/srcbc/x509/store/X509StoreFactory.cs new file mode 100644 index 0000000..2a2c818 --- /dev/null +++ b/src/core/srcbc/x509/store/X509StoreFactory.cs @@ -0,0 +1,44 @@ +using System; +using System.Globalization; + +namespace Org.BouncyCastle.X509.Store +{ + public sealed class X509StoreFactory + { + private X509StoreFactory() + { + } + + public static IX509Store Create( + string type, + IX509StoreParameters parameters) + { + if (type == null) + throw new ArgumentNullException("type"); + + string[] parts = type.ToUpper(CultureInfo.InvariantCulture).Split('/'); + + if (parts.Length < 2) + throw new ArgumentException("type"); + + + switch (parts[0]) + { + case "ATTRIBUTECERTIFICATE": + case "CERTIFICATE": + case "CERTIFICATEPAIR": + case "CRL": + { + if (parts[1] == "COLLECTION") + { + X509CollectionStoreParameters p = (X509CollectionStoreParameters) parameters; + return new X509CollectionStore(p.GetCollection()); + } + break; + } + } + + throw new NoSuchStoreException("X.509 store type '" + type + "' not available."); + } + } +}